我正在学习使用多处理池。我做了这个剧本作为练习。
任何人都可以告诉我为什么使用普通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)
使用池的结果是:332833500时间:0.14810872077941895
不使用池的结果是:332833500时间:0.0005018711090087891
我认为这里有几个原因,但我猜它主要与运行多个进程的开销有关,这主要与同步和通信有关,以及你的非并行化代码是写得更有效率。
作为基础,以下是您的未修改代码在我的计算机上运行的方式:
('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)
所以你有它。您所需要的只是为您的孩子提供更多的工作,而且他们将更有价值。
当使用Pool
时,python使用global interpreter lock来同步多个进程之间的多个线程。也就是说,当一个线程正在运行时,所有其他线程都会停止/等待。因此,您将体验到的是顺序执行,而不是并行执行。在您的示例中,即使您在pool
中的多个线程之间分发,它们也会由于全局解释器锁而顺序运行。此外,这也增加了调度的大量开销。
从全局解释器锁的python docs:
CPython解释器使用的机制,以确保一次只有一个线程执行Python字节码。这通过使对象模型(包括诸如dict的关键内置类型)对于并发访问而言是隐式安全的,从而简化了CPython实现。锁定整个解释器使得解释器更容易是多线程的,代价是多处理器机器提供的大部分并行性。
因此,你所取得的并不是真正的并行性。如果你需要在python中实现真正的多处理功能,你需要使用Processes,这将导致你使用Queues
在进程之间交换数据。
多处理包提供本地和远程并发,通过使用子进程而不是线程有效地侧向执行全局解释器锁。因此,多处理模块允许程序员充分利用给定机器上的多个处理器。
在Python 2.7.16文档的Process-based “threading” interface章节中找到