多处理python:如何拆分作业

时间:2015-08-15 19:25:50

标签: python multiprocessing python-multiprocessing

我尝试编写一个用于多处理的python。我一直在网上阅读,但我仍然不明白如何写它。

我的剧本:

import multiprocessing as mp
import numpy as np
import ctypes

def func(M, j, :
    coor_i = np.zeros(1000)
    coor_j = np.ones(1000) # In reality it is loaded from a txt
    A = np.square(coor_i - coor_j)
    a = A.sum
    M[j] = a

 for i in range(1,100) :
     M = mp.Array(ctypes.c_double, np.ones(i))
     p = mp.Process(target = func, args = (coor_i, j)) for j in range(1,i)

     p.start()
     p.join()

     print(M)

我在线看,我看过 - 'mp.Pool','过程','mp.Queue'

非常感谢。

1 个答案:

答案 0 :(得分:0)

首先,您的第一个错误是不从您的func()函数返回任何内容。在python中,所有值都是对内存中对象的引用,当您执行赋值时,您将使用新对象替换引用的值。因此,M = -M不会改变M的对象,而是创建一个新对象,并在函数范围内更改引用M

这意味着您的功能总是返回None

>>> from multiprocessing import Pool
>>> def func(M): # if you call with M=1
...    # M==1 here
...    M = -M
...    # M==-1 here
...
>>> M = 1
>>> M = func(M)
>>> print(M)
None

要解决此问题,您需要将返回值:

>>> def func(M):
...     return -M
...
>>> print(func(1))
-1

然后,并行化工作的最佳方法是使用一个进程池,以便您可以控制并行运行的实例数,直接调整from the documentation examples

>>> def func(M):
>>>     return -M
... 
>>> pool = Pool(processes=4)                # start 4 worker processes
>>> results = []
>>> for i in range(1,100):
...     results.append(pool.apply_async(func, [i]))    # evaluate "func(i)" asynchronously
...
>>> print [result.get() for result in results]
[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, -60, -61, -62, -63, -64, -65, -66, -67, -68, -69, -70, -71, -72, -73, -74, -75, -76, -77, -78, -79, -80, -81, -82, -83, -84, -85, -86, -87, -88, -89, -90, -91, -92, -93, -94, -95, -96, -97, -98, -99]

另一种做同样事情的方法是使用:

>>> print(pool.map(func, range(1,100)))
[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, -60, -61, -62, -63, -64, -65, -66, -67, -68, -69, -70, -71, -72, -73, -74, -75, -76, -77, -78, -79, -80, -81, -82, -83, -84, -85, -86, -87, -88, -89, -90, -91, -92, -93, -94, -95, -96, -97, -98, -99]

话虽如此,如果你所做的一切确实是在否定价值(或者那些简单的东西) - 我猜你不是 - 那么 NOT 最好使用并行化, python和你的微处理器将矢量化工作并让它运行得更快:

>>> print([-M for M in range(1,100)])
[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, -60, -61, -62, -63, -64, -65, -66, -67, -68, -69, -70, -71, -72, -73, -74, -75, -76, -77, -78, -79, -80, -81, -82, -83, -84, -85, -86, -87, -88, -89, -90, -91, -92, -93, -94, -95, -96, -97, -98, -99]

这是一个指标,在我的机器上用python2运行:

>>> from timeit import timeit
>>> timeit("[-M for M in range(1,100)]", number=100000)
0.6327948570251465
>>> def test():
...     pool.map(func, range(1,100))
...
>>> timeit(test, number=100000)
31.26303195953369

编辑:

你的问题的问题在于,你不清楚你想要实现什么,以及你想要做什么。通常,并行化的第一个途径是执行非并行化的版本,并尝试并行化更好地工作并且没有互斥的事物。

但是在你的代码中让我感到震惊的是,对于range(1,100)循环的每次迭代,你实际上都在等待一个进程完成,然后再启动一个进程:

 for i in range(1,100) :
     M = mp.Array(ctypes.c_double, np.ones(i))
     # Create process
     p = mp.Process(target = func, args = (coor_i, j)) for j in range(1,i)

     # Start a process
     p.start()
     # wait for the process p to finish before going on
     p.join()
     # will continue when p has finished

如果您想解决此问题,可以使用我的示例中显示的Pool或:

 for i in range(1,100) :
     M = mp.Array(ctypes.c_double, np.ones(i))
     # Create process
     p = mp.Process(target = func, args = (coor_i, j)) for j in range(1,i)

     # Start a process
     p.start()
     # wait for the process p to finish before going on
     p.join()
     # will continue when p has finished

因此,快速改进将是:

 processes = [] # keep a list of all the processes
 for i in range(1,100) :
     M = mp.Array(ctypes.c_double, np.ones(i))
     # Create process
     for j in range(1,i):
         p = mp.Process(target = func, args = (coor_i, j))
         # Append to processes list
         processes.append(p)
         # Start a process
         p.start()

# wait for all processes to have finished before quitting (or going on)
for p in processes:
    p.join() 

所以我刚刚更新了代码:你在一个循环中启动所有进程,而在另一个循环中你阻塞直到它们完成。 .join()正在阻止当前进程阻塞,直到另一个进程完成,因此第二个循环确保每个进程在完成代码片段之前已完成,或继续执行更多代码。

因此,对于“生成器”问题,我没有注意到您在for j in range(1, i)行上执行了mp.Process,因此没有将进程附加到进程列表,而是添加了列表生成器。 / p>

实际情况是你的conde 无法工作,并且在编译时会失败,因为:

p = Process(target = func, args = (coor_i, j)) for j in range(1 ,i)
                                               ^
SyntaxError: invalid syntax

如果您通过以下方式更正:

>>> (p = Process(target = func, args = (coor_i, j)) for j in range(1 ,i))

然后您的原始代码无法工作,因为p不是进程,而是列表生成器且p.start()不存在:

>>> p.next()
<Process(Process-5, initial)>
>>> p.next()
<Process(Process-6, initial)>
>>> p.next()
<Process(Process-7, initial)>
>>> p.next()
<Process(Process-8, initial)>
>>> p.next()
<Process(Process-9, initial)>

因此,在这种情况下使用for one liner不是一个坏主意,而是使用传统方法进行修正。