我正在尝试在Python中使用多处理池。这是我的代码:
def f(x):
return x
def foo():
p = multiprocessing.Pool()
mapper = p.imap_unordered
for x in xrange(1, 11):
res = list(mapper(f,bar(x)))
当xrange
像xrange(1, 6)
一样小时,此代码使用所有CPU(我有8个CPU)。但是,当我将范围增加到xrange(1, 10)
时。我观察到只有1个CPU以100%运行,而其余的只是空转。可能是什么原因?是因为,当我增加范围时,操作系统会因过热而关闭CPU吗?
如何解决此问题?
为了复制我的问题,我创建了这个例子:它是一个从字符串问题生成的简单ngram。
#!/usr/bin/python
import time
import itertools
import threading
import multiprocessing
import random
def f(x):
return x
def ngrams(input_tmp, n):
input = input_tmp.split()
if n > len(input):
n = len(input)
output = []
for i in range(len(input)-n+1):
output.append(input[i:i+n])
return output
def foo():
p = multiprocessing.Pool()
mapper = p.imap_unordered
num = 100000000 #100
rand_list = random.sample(xrange(100000000), num)
rand_str = ' '.join(str(i) for i in rand_list)
for n in xrange(1, 100):
res = list(mapper(f, ngrams(rand_str, n)))
if __name__ == '__main__':
start = time.time()
foo()
print 'Total time taken: '+str(time.time() - start)
当num
很小(例如num = 10000
)时,我发现所有8个CPU都被使用了。但是,当num
非常大时(例如,num = 100000000
)。仅使用2个CPU,其余为空闲。这是我的问题。
警告:当num
过大时,系统/虚拟机可能会崩溃。
答案 0 :(得分:7)
首先,ngrams
本身需要花费很多时间。当这种情况发生时,它显然只有一个核心。但即使完成(通过在ngrams
之外移动mapper
电话并在其之前和之后投放print
非常容易进行测试),您还可以< em>仍仅使用一个核心。我得到1核100%,其他核心大约2%。
如果你在Python 3.4中尝试相同的东西,情况会有所不同 - 我仍然得到100%的1核心,但其他的是15-25%。
那么,发生了什么?好吧,在multiprocessing
中,传递参数和返回值总是有些开销。在您的情况下,这种开销完全淹没了实际工作,这只是return x
。
以下是开销如何工作:主进程必须挑选值,然后将它们放在队列中,然后等待另一个队列上的值并取消它们。每个子进程在第一个队列上等待,取消排序值,执行任何操作,修补值,并将它们放在另一个队列中。必须同步对队列的访问(在大多数非Windows平台上通过POSIX信号量,我认为在Windows上是NT内核互斥锁。)
据我所知,您的流程花费了99%以上的时间等待队列或阅读或写作。
这并不是太意外,因为您需要处理大量数据,除了对这些数据进行酸洗和取消排序之外,根本不需要计算。
如果您查看CPython 2.7中SimpleQueue
的来源,则会在持有锁的情况下进行酸洗和去除。因此,几乎所有的后台进程都会在锁定时发生,这意味着它们最终都会在一个核心上进行序列化。
但是在CPython 3.4中,在锁定之外发生了酸洗和去除。而且显然足以使用15-25%的核心。 (我相信这种变化发生在3.2,但是我懒得跟踪它。)
尽管如此,即使在3.4上,你也要花费更多时间等待访问队列而不是做任何事情,甚至是multiprocessing
开销。这就是为什么核心只能达到25%。
当然,您在开销上花费的时间比实际工作多了几个数量级,这使得这不是一个很好的测试,除非您尝试测试可以获得的最大吞吐量。您机器上的特定multiprocessing
实施或其他内容。
一些观察结果:
chunksize=1000
或类似的帮助),这可能会解决您的大多数问题multiprocessing
或第三方multiprocessing
库之一的后端,只是为了移动腌出来的锁。答案 1 :(得分:2)
问题是你的f()
函数(在不同的进程上运行的函数)没有做任何特殊的事情,因此它没有给CPU加载。
ngrams()
正在做一些重的&#34;计算,但是你在主进程上调用此函数,而不是在池中。
为了使事情更清楚,请考虑这段代码......
for n in xrange(1, 100):
res = list(mapper(f, ngrams(rand_str, n)))
......等同于:
for n in xrange(1, 100):
arg = ngrams(rand_str, n)
res = list(mapper(f, arg))
以下是在主进程上执行的CPU密集型操作:
num = 100000000
rand_list = random.sample(xrange(100000000), num)
您应该更改代码,以便在池中调用sample()
和ngrams()
,或者更改f()
以便它执行CPU密集型操作,并且您可以看到所有CPU的负载很高。