29

我一直在阅读有关 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

任何帮助,将不胜感激。

4

3 回答 3

37

是的,可以对多个进程进行此求和,非常类似于使用多个线程进行求和:

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()

但是,使用多个进程执行此操作可能比在单个进程中执行此操作要慢,因为来回复制数据比立即求和更昂贵。

于 2009-07-25T15:48:51.047 回答
22

欢迎来到并发编程的世界。

Python 能(和不能)做什么取决于两件事。

  1. 操作系统可以(和不能)做什么。大多数操作系统将进程分配给内核。要使用 4 个核心,您需要将问题分解为四个进程。这比听起来容易。有时。

  2. 底层 C 库可以(和不能)做什么。如果 C 库公开了 OS 的功能并且 OS 公开了硬件的功能,那么你是可靠的。

将一个问题分解为多个进程——尤其是在 GNU/Linux 中——很容易。将其分解为多步骤管道。

在求和一百万个数字的情况下,请考虑以下 shell 脚本。假设一些假设sum.py程序对标准输入上的数字范围或数字列表求和。

( sum.py 0 500000 & sum.py 50000 1000000 ) | 总和.py

这将有 3 个并发进程。两个是对很多数字求和,第三个是对两个数字求和。

由于 GNU/Linux shell 和操作系统已经为您处理了部分并发,您可以设计简单(非常、非常简单)的程序,从标准输入读取、写入标准输出,并设计用于完成大型工作的一小部分。

您可以尝试通过使用subprocess构建管道而不是将作业分配给 shell 来减少开销。但是,您可能会发现 shell 非常非常快速地构建管道。(它是直接用 C 语言编写的,并为您直接调用 OS API。)

于 2009-07-25T15:45:49.867 回答
8

当然可以,例如:

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

请注意,这种粒度太细了,不值得生成进程——整体求和任务很小(这就是为什么我可以重新计算 main 中的总和作为检查;-)并且来回移动了太多数据(在事实上,子进程不需要获取他们处理的子列表的副本——索引就足够了)。因此,这是一个“玩具示例”,其中并不真正需要多处理。但是,使用不同的架构(使用从队列接收多个任务以执行的子进程池、​​最小化来回数据移动等)以及在粒度较小的任务上,您实际上可以获得性能方面的好处。

于 2009-07-25T15:55:45.913 回答