我对Python多处理感到困惑。
我正在尝试加速从数据库处理字符串的函数,但我必须误解多处理是如何工作的,因为函数在给予工作池时比“正常处理”需要更长的时间。
这是我想要实现的一个例子。
from time import clock, time
from multiprocessing import Pool, freeze_support
from random import choice
def foo(x):
TupWerteMany = []
for i in range(0,len(x)):
TupWerte = []
s = list(x[i][3])
NewValue = choice(s)+choice(s)+choice(s)+choice(s)
TupWerte.append(NewValue)
TupWerte = tuple(TupWerte)
TupWerteMany.append(TupWerte)
return TupWerteMany
if __name__ == '__main__':
start_time = time()
List = [(u'1', u'aa', u'Jacob', u'Emily'),
(u'2', u'bb', u'Ethan', u'Kayla')]
List1 = List*1000000
# METHOD 1 : NORMAL (takes 20 seconds)
x2 = foo(List1)
print x2[1:3]
# METHOD 2 : APPLY_ASYNC (takes 28 seconds)
# pool = Pool(4)
# Werte = pool.apply_async(foo, args=(List1,))
# x2 = Werte.get()
# print '--------'
# print x2[1:3]
# print '--------'
# METHOD 3: MAP (!! DOES NOT WORK !!)
# pool = Pool(4)
# Werte = pool.map(foo, args=(List1,))
# x2 = Werte.get()
# print '--------'
# print x2[1:3]
# print '--------'
print 'Time Elaspse: ', time() - start_time
我的问题:
答案 0 :(得分:2)
所以你的第一个问题是在foo(x)
中没有发生实际的并行性,你将整个列表传递给函数一次。
1) 进程池的想法是让许多进程在某些数据的不同位上进行计算。
# METHOD 2 : APPLY_ASYNC
jobs = 4
size = len(List1)
pool = Pool(4)
results = []
# split the list into 4 equally sized chunks and submit those to the pool
heads = range(size/jobs, size, size/jobs) + [size]
tails = range(0,size,size/jobs)
for tail,head in zip(tails, heads):
werte = pool.apply_async(foo, args=(List1[tail:head],))
results.append(werte)
pool.close()
pool.join() # wait for the pool to be done
for result in results:
werte = result.get() # get the return value from the sub jobs
如果处理每个块所需的时间大于启动过程所需的时间,这只会给你一个实际的加速,在四个过程和四个工作要完成的情况下,当然这些动态变化如果你有4个流程和100个工作要做。请记住,您正在创建一个全新的python解释器四次,这不是免费的。
2)你对map有的问题是它在foo
的单独进程中将List1
应用于每个元素,这将花费很长时间。因此,如果您的池有4个进程map
将弹出列表中的项目四次并将其发送到要处理的进程 - 等待进程完成 - 弹出更多的列表 - 等待过程结束。这仅在处理单个项目需要很长时间时才有意义,例如,如果每个项目都是指向一个千兆字节文本文件的文件名。但是,就地图而言,只需要列出一个列表并将其传递给foo
,其中apply_async
占据列表的一部分。请尝试以下代码
def foo(thing):
print thing
map(foo, ['a','b','c','d'])
这是内置的python地图,将运行一个进程,但多进程版本的想法完全相同。
根据J.F.Sebastian的评论添加:但您可以使用chunksize
参数map
来指定每个块的大致大小。
pool.map(foo, List1, chunksize=size/jobs)
我不知道Windows上map
是否存在问题,因为我没有可用于测试的问题。
3)是的,因为你的问题足够大,足以证明你有新的python解释器
4)无法给出明确的答案,因为它取决于核心/处理器的数量等。但一般情况下它应该在Windows上正常。
答案 1 :(得分:0)
问题(2) 在Dougal和Matti的指导下,我弄清楚出了什么问题。 原始foo函数处理列表列表,而map需要一个函数来处理单个元素。
新功能应该是
def foo2 (x):
TupWerte = []
s = list(x[3])
NewValue = choice(s)+choice(s)+choice(s)+choice(s)
TupWerte.append(NewValue)
TupWerte = tuple(TupWerte)
return TupWerte
jobs = 4
size = len(List1)
pool = Pool()
#Werte = pool.map(foo2, List1, chunksize=size/jobs)
Werte = pool.map(foo2, List1)
pool.close()
print Werte[1:3]
感谢所有帮助我理解这一点的人。
所有方法的结果: for List * 2 Mio记录:正常13.3秒,与异步并行:7.5秒,与带有chuncksize的地图并行:7.3,没有chunksize 5.2秒
答案 2 :(得分:-1)
如果您感兴趣,这是一个通用的多处理模板。
import multiprocessing as mp
import time
def worker(x):
time.sleep(0.2)
print "x= %s, x squared = %s" % (x, x*x)
return x*x
def apply_async():
pool = mp.Pool()
for i in range(100):
pool.apply_async(worker, args = (i, ))
pool.close()
pool.join()
if __name__ == '__main__':
apply_async()
输出如下:
x= 0, x squared = 0
x= 1, x squared = 1
x= 2, x squared = 4
x= 3, x squared = 9
x= 4, x squared = 16
x= 6, x squared = 36
x= 5, x squared = 25
x= 7, x squared = 49
x= 8, x squared = 64
x= 10, x squared = 100
x= 11, x squared = 121
x= 9, x squared = 81
x= 12, x squared = 144
正如您所看到的,数字不是有序的,因为它们是异步执行的。