Python 3 多处理池

Python 3 Multiprocessing Pool

我正在学习将池与多处理一起使用。我把这个脚本作为练习。

谁能告诉我为什么使用普通的 for 循环比使用池花费的时间更少?

P.S:我的CPU有2个核心。

非常感谢。

from multiprocessing import Pool
from functools import reduce
import time

def one(n):
    a = n*n
    return a 

if __name__ == '__main__':
    l = list(range(1000))

    p = Pool()
    t = time.time()
    pol = p.map(one, l)
    result = reduce(lambda x,y: x+y, pol)
    print("Using Pool the result is: ", result, "Time: ", time.time() - t )
    p.close()
    p.join()

    def two(n):
        t = time.time()
        p_result = [] 

        for i in n:
            a = i*i 
            p_result.append(a)

        result = reduce(lambda x,y: x+y, p_result)
        print("Not using Pool the result is: ", result, "Time: ", time.time() - t)

    two(l)

使用 Pool 结果是: 332833500 时间: 0.14810872077941895

不使用池结果是: 332833500 时间: 0.0005018711090087891

当使用Pool时,python使用global interpreter lock在多个进程之间同步多个线程。也就是说,当一个线程是运行ning时,所有其他线程都是stopped/waiting。因此,您将体验到的是顺序执行,而不是并行执行。在您的示例中,即使您在 pool 中的多个线程之间进行分配,由于 全局解释器锁 ,它们也会按顺序 运行。这也增加了很多调度开销。

来自python关于全局解释器锁的文档:

The mechanism used by the CPython interpreter to assure that only one thread executes Python bytecode at a time. This simplifies the CPython implementation by making the object model (including critical built-in types such as dict) implicitly safe against concurrent access. Locking the entire interpreter makes it easier for the interpreter to be multi-threaded, at the expense of much of the parallelism afforded by multi-processor machines.

因此,您实现的并不是真正的并行性。如果您需要在 python 中实现真正的多处理能力,您需要使用 Processes,这将导致您使用 Queues 在进程之间交换数据。

我认为这里有几个原因,但我猜这主要与 运行 多个进程的开销有关,这主要与同步和通信有关因为您的非并行代码编写效率更高。

作为基础,以下是您未修改的代码 运行 在我的计算机上的显示方式:

('Using Pool the result is: ', 332833500, 'Time: ', 0.0009129047393798828)
('Not using Pool the result is: ', 332833500, 'Time: ', 0.000598907470703125)

首先,我想通过使 two() 函数的代码与并行代码几乎相同来尝试公平竞争。这是修改后的 two() 函数:

def two(l):
    t = time.time()

    p_result = map(one, l)

    result = reduce(lambda x,y: x+y, p_result)
    print("Not using Pool the result is: ", result, "Time: ", time.time() - t)

现在,这在这种情况下实际上并没有太大的不同,但稍后看到这两种情况都在做完全相同的事情将很重要。以下是此更改的示例输出:

('Using Pool the result is: ', 332833500, 'Time: ', 0.0009338855743408203)
('Not using Pool the result is: ', 332833500, 'Time: ', 0.0006031990051269531)

我现在想说明的是,由于 one() 函数的计算成本非常低,进程间通信的开销超过了 运行 并行处理它的好处。我将按如下方式修改 one() 函数以强制它执行大量额外计算。请注意,由于 two() 函数的更改,此更改将同时影响并行和单线程代码。

def one(n):
    for i in range(100000):
        a = n*n
    return a

之所以有for循环,是为了给每个进程一个存在的理由。因为你有你的原始代码,每个进程简单地做几次乘法,然后必须将结果列表发送回父进程,并等待被赋予一个新的块。发送和等待比完成单个块花费的时间要长得多。通过添加这些额外的周期,它迫使每个块花费更长的时间,而不会改变进程间通信所需的时间,因此我们开始看到并行性的回报。这是我 运行 对 one() 函数进行此更改的代码时的结果:

('Using Pool the result is: ', 332833500, 'Time: ', 1.861448049545288)
('Not using Pool the result is: ', 332833500, 'Time: ', 3.444211959838867)

好了。您所需要的只是给您的子进程更多的工作,他们将更值得您花时间。

The multiprocessing package offers both local and remote concurrency, effectively side-stepping the Global Interpreter Lock by using subprocesses instead of threads. Due to this, the multiprocessing module allows the programmer to fully leverage multiple processors on a given machine.

在 Python 2.7.16 文档的第 Process-based “threading” interface 章中找到