2014-03-25 42 views
0

我對並行化完全陌生。我想平行嵌套for循環並存儲一些中間結果。結果來自一個函數f,它取自一些形式參數和一些來自全局變量的值。我從here得到了一些建議,例如我使用itertools來生成相當於嵌套循環的笛卡爾積。但它似乎並不奏效。我想要存儲中間結果的數組保持不變。附上最簡單的工作示例。試圖平行嵌套的for循環並保存中間結果

操作系統:Windows 7 64位

Python發行:天幕Enthought

import itertools 
import numpy as np 
from multiprocessing import Pool 

list1 = range(4, 8) 
list2 = range(6, 9) 
ary = np.zeros((len(list1), len(list2))) 

#This is the archetypical function f. It DOES NOT have p2 as a parameter! This 
#is intended! In my (more complex) program a function f calls somewhere deep 
#down another function that gets its values from global variables. Rewriting 
#the code to hand down the variables as parameters would turn my code into a mess. 
def f(p1): 
    return p1*p2 

#This is what I want to parallelize: a nested loop, where the result of f is saved 
#in an array element corresponding to the indices of p1 and p2. 
#for p1 in list1: 
# for p2 in list2: 
#  i = list1.index(p1) 
#  j = list2.index(p2) 
#  ary[i,j]=f(p1) 

#Here begins the try to parallelize the nested loop. The function g calls f and 
#does the saving of the results. g takes a tuple x, unpacks it, then calculates 
#f and saves the result in an array. 
def g(x): 
    a, b = x 
    i = list1.index(a) 
    j = list2.index(b) 
    global p2 
    p2 = b 
    ary[i,j] = f(a) 

if __name__ == "__main__": 
    #Produces a cartesian product. This is equivalent to a nested loop. 
    it = itertools.product(list1, list2) 
    pool = Pool(processes=2) 
    result = pool.map(g, it) 
    print ary 
    #Result: ary does not change! 

回答

1

通過使用Pool,你的程序以某種方式複製過程的次數,他們每個人有自己的全球變量。當您的計算返回時,主進程的全局變量不會改變。 你應該用你的函數的返回值,你在並行調用和合並結果,這意味着,使用可變result從線

result = pool.map(g, it) 

在你的情況下,它僅包含的None列表這麼遠。

並行化的一般提示:始終使用純粹的計算,也就是說,不要像全局變量那樣依賴副作用。

+0

好的,你說我不應該依賴ary的「全局性」。好,我在g()ary [i,j] = f(a)的定義中用 'return(i,j,f(a))替換' 然後結果不包含'None',我可以構造從結果ary。我定義了一個函數 'def constr(x): i,j,val = x ary [i,j] = val' 然後我可以將結構映射到結果上並得到ary。有用!謝謝!關於全局性:是否可以導致競爭條件,即一個進程獲得p2的'錯誤'值,因爲另一個進程更改了其(全局值)?p2 p2 = b' – hauntergeist

+0

@David:如果你接受我的回答,會很好。對你的評論:我想,這不會發生,但是,再次,有一個'全球',試圖重寫。 – spiehr

+0

我做了一個測試:我在代碼的開頭設置了p2 = 0。然後執行並行部分。 ary被正確構建,並且p2的(全局)值保持爲0.我想每個進程都會得到list1,list2,ary,p2等的副本,並且可以在不修改原始進程或其他進程的副本的情況下對其進行修改。它似乎工作,但對我來說仍然有點神奇。我不能在沒有'全局'的情況下重寫我的真實代碼,因爲需要這些全局值的函數嵌套在其他函數中。重寫現有的代碼將是一場超越其好處的噩夢。 – hauntergeist

0

您需要使用某種機制在進程之間共享信息。例如看看multiprocessing.queue。

如果您想使用共享內存,則需要使用線程代替。您可能會發現雖然GIL確實會影響線程性能,但您仍然可以並行運行numpy命令。

+0

共享內存不需要線程。共享內存也可以在多個進程中使用。例如。 MMAP。mmap從分頁文件(Windows上的fd 0或Linux上的-1),Linux上的/ tmp中的mmap,使用SysV shm_get或POSIX shm_open。一旦你有一個緩衝區,它可以用來支持指向共享內存的NumPy數組(numpy.frombuffer)。 multiprocessing.Array也可以用作NumPy的共享內存緩衝區。 –

+0

這難道不就像用大錘殺蒼蠅嗎?線程,隊列等都有同步方法,這是原始問題。 – Claris

+0

使用multiprocessing.Array作爲輸出數組的緩衝區可以解決問題。它沒有更新,因爲它在本地進程中。將它放在共享內存中是一種可能的補救措施。由於GIL,線程不可用於計算任務的並行化。 Python線程可以並行處理阻塞的I/O操作,但不是CPU密集型工作。 –