并行启动Python中的进程串行执行?

时间:2012-06-04 18:11:35

标签: python parallel-processing python-3.x multiprocessing

我对Python的多处理库有一个理解问题/问题:
为什么不同的过程(几乎)同时开始,至少似乎是串行执行而不是并行执行?

任务是控制大量粒子的宇宙(粒子是一组x / y / z坐标和质量),并在利用多处理器环境的同时对它们进行各种分析。特别是对于下面显示的例子,我想计算所有粒子的质量中心 因为任务明确地说要使用多个处理器,所以我没有使用线程库,因为有这个GIL-thingy将执行限制在一个处理器上。
这是我的代码:

from multiprocessing import Process, Lock, Array, Value
from random import random
import math
from time import time

def exercise2(noOfParticles, noOfProcs):
    startingTime = time()
    particles = []
    processes = []
    centerCoords = Array('d',[0,0,0])
    totalMass = Value('d',0)
    lock = Lock()

    #create all particles
    for i in range(noOfParticles):
        p = Particle()
        particles.append(p)

    for i in range(noOfProcs):
        #determine the number of particles every process needs to analyse
        particlesPerProcess = math.ceil(noOfParticles / noOfProcs)
        #create noOfProcs Processes, each with a different set of particles        
        p = Process(target=processBatch, args=(
            particles[i*particlesPerProcess:(i+1)*particlesPerProcess],
            centerCoords, #handle to shared memory
            totalMass, #handle to shared memory
            lock, #handle to lock
            'batch'+str(i)), #also pass name of process for easier logging
            name='batch'+str(i))
        processes.append(p)
        print('created proc:',i)

    #start all processes
    for p in processes:
        p.start() #here, the program waits for the started process to terminate. why?

    #wait for all processes to finish
    for p in processes:
        p.join()

    #normalize the coordinates
    centerCoords[0] /= totalMass.value
    centerCoords[1] /= totalMass.value
    centerCoords[2] /= totalMass.value

    print(centerCoords[:])
    print('total time used', time() - startingTime, ' seconds')


class Particle():
    """a particle is a very simple physical object, having a set of x/y/z coordinates and a mass.
    All values are randomly set at initialization of the object"""

    def __init__(self):
        self.x = random() * 1000
        self.y = random() * 1000
        self.z = random() * 1000
        self.m = random() * 10

    def printProperties(self):
        attrs = vars(self)
        print ('\n'.join("%s: %s" % item for item in attrs.items()))

def processBatch(particles,centerCoords,totalMass,lock,name):
    """calculates the mass-weighted sum of all coordinates of all particles as well as the sum of all masses.
    Writes the results into the shared memory centerCoords and totalMass, using lock"""

    print(name,' started')
    mass = 0
    centerX = 0
    centerY = 0
    centerZ = 0

    for p in particles:
        centerX += p.m*p.x
        centerY += p.m*p.y
        centerZ += p.m*p.z
        mass += p.m

    with lock:
        centerCoords[0] += centerX
        centerCoords[1] += centerY
        centerCoords[2] += centerZ
        totalMass.value += mass

    print(name,' ended')

if __name__ == '__main__':
    exercise2(2**16,6)

现在我希望所有进程几乎同时启动并并行执行。但是,当我查看程序的输出时,看起来好像进程是串行执行的:

created proc: 0
created proc: 1
created proc: 2
created proc: 3
created proc: 4
created proc: 5
batch0  started
batch0  ended
batch1  started
batch1  ended
batch2  started
batch2  ended
batch3  started
batch3  ended
batch4  started
batch4  ended
batch5  started
batch5  ended
[499.72234074100135, 497.26586187539453, 498.9208784328791]
total time used 4.7220001220703125  seconds

当使用Eclipse调试器逐步执行程序时,我可以看到程序如何总是等待一个进程终止,然后在标记为以“why?”结尾的注释的行开始下一个进程。当然,这可能只是调试器,但是当我查看正常运行中产生的输出时,这显示了上面的图片。

  • 这些进程是否并行执行,由于stdout的某些共享问题,我无法看到它?
  • 如果进程是串行执行的:为什么?我怎样才能让它们并行运行?

非常感谢任何理解这一点的帮助。

我在具有双核Intel处理器的Windows 7计算机上使用Python 3.2.3从PyDev和命令行执行了上述代码。


编辑:
由于程序的输出,我误解了问题:进程实际上是并行运行的,但是大量数据的腌制并将其发送到子进程的开销需要很长时间才能完全扭曲图像。
将粒子(即数据)的创建移动到子过程,使得它们不必首先被腌制,从而消除了所有问题并导致程序的有用并行执行。
因此,为了解决这个问题,我必须将粒子保存在共享内存中,这样就不必将它们传递给子进程。

1 个答案:

答案 0 :(得分:2)

我在我的系统上运行你的代码(Python 2.6.5)并且它几乎立即返回结果,这让我觉得你的任务大小可能很小,以至于进程在下一个开始之前就完成了(注意启动一个进程比启动线程慢。我对结果中的total time used 4.7220001220703125 seconds提出质疑,因为这比我的系统运行相同的代码要长40倍。我将粒子数量增加到2**20,得到了以下结果:

('created proc:', 0)
('created proc:', 1)
('created proc:', 2)
('created proc:', 3)
('created proc:', 4)
('created proc:', 5)
('batch0', ' started')
('batch1', ' started')
('batch2', ' started')
('batch3', ' started')
('batch4', ' started')
('batch5', ' started')
('batch0', ' ended')
('batch1', ' ended')
('batch2', ' ended')
('batch3', ' ended')
('batch5', ' ended')
('batch4', ' ended')
[500.12090773656854, 499.92759577086059, 499.97075039983588]
('total time used', 5.1031057834625244, ' seconds')

这更符合我的预期。如果增加任务规模,你会得到什么?