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 章中找到
我正在学习将池与多处理一起使用。我把这个脚本作为练习。
谁能告诉我为什么使用普通的 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 章中找到