Cython啓動器在這裏。我試圖通過使用多個線程來加速計算某些成對統計量(在幾個分箱中)。特別是,我使用cython.parallel中的prange,它在內部使用openMP。Cython:使prange並行化線程安全
下面的最小例子說明了這個問題(通過Jupyter筆記本Cython magic編譯)。
筆記本設置:
%load_ext Cython
import numpy as np
用Cython代碼:
%%cython --compile-args=-fopenmp --link-args=-fopenmp -a
from cython cimport boundscheck
import numpy as np
from cython.parallel cimport prange, parallel
@boundscheck(False)
def my_parallel_statistic(double[:] X, double[:,::1] bins, int num_threads):
cdef:
int N = X.shape[0]
int nbins = bins.shape[0]
double Xij,Yij
double[:] Z = np.zeros(nbins,dtype=np.float64)
int i,j,b
with nogil, parallel(num_threads=num_threads):
for i in prange(N,schedule='static',chunksize=1):
for j in range(i):
#some pairwise quantities
Xij = X[i]-X[j]
Yij = 0.5*(X[i]+X[j])
#check if in bin
for b in range(nbins):
if (Xij < bins[b,0]) or (Xij > bins[b,1]):
continue
Z[b] += Xij*Yij
return np.asarray(Z)
模擬數據和箱
X = np.random.rand(10000)
bin_edges = np.linspace(0.,1,11)
bins = np.array([bin_edges[:-1],bin_edges[1:]]).T
bins = bins.copy(order='C')
時序經由
%timeit my_parallel_statistic(X,bins,1)
%timeit my_parallel_statistic(X,bins,4)
個
產量
1 loop, best of 3: 728 ms per loop
1 loop, best of 3: 330 ms per loop
這是不是一個完美的比例,但是這不是問題的重點。 (但不要讓我知道,如果你有超越添加常用的裝飾或微調PRANGE參數的建議。)
然而,這種計算顯然不是線程安全的:
Z1 = my_parallel_statistic(X,bins,1)
Z4 = my_parallel_statistic(X,bins,4)
np.allclose(Z1,Z4)
透着顯著差異在這兩個結果之間(在這個例子中高達20%)。
我強烈懷疑,問題是,多個線程可以在同一時間做
Z[b] += Xij*Yij
。但是我不知道如何在不犧牲加速的情況下解決這個問題。
在我的實際使用情況中,Xij和Yij的計算更加昂貴,因此我希望每對執行一次。此外,預先計算和存儲所有對的Xij和Yij,然後簡單地循環通過bin不是一個好選擇,因爲N可以變得非常大,並且我不能在內存中存儲100,000 x 100,000個numpy數組(這實際上是在Cython中重寫它的主要動機!)。
系統信息(中添加註釋如下建議):
CPU(s): 8
Model name: Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
OS: Red Hat Linux v6.8
Memory: 16 GB
每個線程中的動作是否真的獨立於任何其他動作?首先運行哪一個是否重要?如果存在任何類型的依賴性,這不適合並行操作。 – hpaulj
只要每個線程創建自己的Xij和Yij,他們應該是獨立的(但也許這是問題?)就數學而言,Xij和Yij獨立計算每對(i,j) ,因此也是對統計量Z的貢獻。 – user4319496
謝謝你在你的問題中包含如此出色的[mcve]!這樣一個經過深入研究和制定的問題在SO上太稀缺了。你可能包含的唯一東西是你的CPU模型和內存來評論性能,但這不是問題的主要觀點。 – Zulan