2012-06-04 68 views
2

我對Python的多處理庫有一個瞭解問題/問題:
爲什麼不同的進程同時啓動(幾乎)同步,至少似乎是串行執行而不是並行執行?
並行啓動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) 

現在我所期待的所有進程開始在大約相同的時間和並行執行。但是,當我看PROGRAMM的輸出,這看起來彷彿過程中連續地執行:

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的調試器PROGRAMM步進的時候,我可以看到程序總是等待一個過程終止,然後開始標記以'why?'結尾的評論的下一行。當然,這可能只是調試器,但是當我查看正常運行時產生的輸出時,這顯示了上面的圖片。

  • 這些進程是否並行執行,並且由於stdout的某些共享問題我無法看到它?
  • 如果進程正在串行執行:爲什麼?我怎樣才能讓它們平行運行?

任何幫助理解這一點非常感謝。

我在帶有雙核英特爾處理器的Windows 7計算機上使用Python 3.2.3從PyDev和命令行執行上述代碼。


編輯:
由於我誤解了問題的程序的輸出:該進程是並行實際運行,但酸洗大量的數據並將其發送到子進程的開銷需要很長時間它完全扭曲了圖片。
將粒子(即數據)的創建移動到子過程中,以便它們不必首先被醃製,從而消除了所有問題並且導致程序的有用並行執行。
爲了解決這個任務,我將因此不得不將粒子保存在共享內存中,以便它們不必傳遞給子進程。

回答

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

這更符合我所期望的那樣。如果增加任務大小,你會得到什麼?

+0

嗨布倫丹。非常感謝你的幫助。你的電腦似乎比我的電腦強大得多,因爲我的電腦真的能處理這個任務規模。在兩個過程之間,存在大約一秒的差距。當我添加更多粒子時,其行爲是相同的,但完成需要70秒。特別的是,差距在一個進程結束和下一個進程之間,而不是在同一進程的「開始」和「結束」消息之間 –

+0

請問您可以修改代碼以在開始和結束時打印時間戳每個進程的?也就是說,在'processBatch'函數的'print(...)'語句中添加第三個參數'time()'。 –

+0

這是一個非常好的主意! 'batch0開始在時間11.016999959945679 batch0在時間結束11.111000061035156 BATCH1開始在時間11.046000003814697 BATCH1在時間11.141000032424927 結束... [等等] ...' 我現在可以看到所有進程確實平行運行。有趣的是,'batch0開始'和'batch0結束'幾乎同時出現,'batch0'輸出結束後約10秒,沒有任何反應。並且只有在那個時間之後,batch1開始出現。消息之間的差距與問題的大小成正比 –