Python: 多核处理?
我最近在研究Python的多进程模块,但是我还是不太明白它到底能做些什么。
假设我有一个四核的处理器,还有一个包含1,000,000个整数的列表,我想要计算这些整数的总和。我可以简单地这样做:
list_sum = sum(my_list)
但是这样做只会用到一个核心。
那么,使用多进程模块,有没有办法把这个数组分开,让每个核心计算自己那部分的和,然后把结果返回,这样就可以计算出总和呢?
大概是这样的:
core1_sum = sum(my_list[0:500000]) #goes to core 1
core2_sum = sum(my_list[500001:1000000]) #goes to core 2
all_core_sum = core1_sum + core2_sum #core 3 does final computation
任何帮助都非常感谢。
3 个回答
当然,可以举个例子:
from multiprocessing import Process, Queue
thelist = range(1000*1000)
def f(q, sublist):
q.put(sum(sublist))
def main():
start = 0
chunk = 500*1000
queue = Queue()
NP = 0
subprocesses = []
while start < len(thelist):
p = Process(target=f, args=(queue, thelist[start:start+chunk]))
NP += 1
print 'delegated %s:%s to subprocess %s' % (start, start+chunk, NP)
p.start()
start += chunk
subprocesses.append(p)
total = 0
for i in range(NP):
total += queue.get()
print "total is", total, '=', sum(thelist)
while subprocesses:
subprocesses.pop().join()
if __name__ == '__main__':
main()
结果是:
$ python2.6 mup.py
delegated 0:500000 to subprocess 1
delegated 500000:1000000 to subprocess 2
total is 499999500000 = 499999500000
注意,这种细粒度的处理方式不值得为了它而创建多个进程——因为整体的求和任务很小(这也是我可以在主程序中重新计算总和来检查的原因;-)),而且数据在进程之间来回移动太多(实际上,子进程并不需要获取它们处理的子列表的副本——只需要索引就可以了)。所以,这只是一个“玩具示例”,在这种情况下,使用多进程并没有真正的必要。不过,如果使用不同的架构(比如使用一个进程池,让多个子进程从队列中接收多个任务来执行,减少数据的来回移动等等),在处理更大或更复杂的任务时,实际上是可以提高性能的。
欢迎来到并发编程的世界。
Python能做什么(和不能做什么)主要取决于两个方面。
操作系统能做什么(和不能做什么)。大多数操作系统会把进程分配到不同的核心上。如果你想使用4个核心,你需要把你的问题拆分成四个进程。这听起来比实际操作要简单。有时候。
底层的C库能做什么(和不能做什么)。如果C库能利用操作系统的功能,而操作系统又能利用硬件的功能,那就没问题了。
在GNU/Linux中,把一个问题拆分成多个进程是很简单的。你可以把它分成一个多步骤的管道。
比如说,要对一百万个数字求和,可以想象下面这个shell脚本。假设有一个假想的sum.py
程序,它可以对一系列数字或从标准输入读取的数字进行求和。
( sum.py 0 500000 & sum.py 500000 1000000 ) | sum.py
这样就会有三个并发进程。前两个在对很多数字求和,第三个在对两个数字求和。
由于GNU/Linux的shell和操作系统已经为你处理了一些并发的部分,你可以设计一些简单(非常非常简单)的程序,它们从标准输入读取数据,写入标准输出,并且专门处理大任务中的小部分。
你可以尝试使用subprocess来构建管道,以减少开销,而不是把工作交给shell。不过,你可能会发现,shell构建管道的速度非常非常快。(它是用C语言直接编写的,并且为你直接调用操作系统的API。)
是的,可以通过多个进程来进行这个求和,就像使用多个线程一样:
from multiprocessing import Process, Queue
def do_sum(q,l):
q.put(sum(l))
def main():
my_list = range(1000000)
q = Queue()
p1 = Process(target=do_sum, args=(q,my_list[:500000]))
p2 = Process(target=do_sum, args=(q,my_list[500000:]))
p1.start()
p2.start()
r1 = q.get()
r2 = q.get()
print r1+r2
if __name__=='__main__':
main()
不过,用多个进程来做可能会比用一个进程要慢,因为在不同进程之间传输数据的成本比直接求和要高。