2017-02-09 55 views
0

我使用PRANGE過像這樣的列表迭代:Cython並行prange - 線程局部性?

from cython.parallel import prange, threadid 

cdef int tid 
cdef CythonElement tEl 
cdef int a, b, c 

# elList: python list of CythonElement instances is passed via function call 
for n in prange(nElements, schedule='dynamic', nogil=True): 
    with gil: 
     tEl = elList[n] 
     tid = threadid() 
     a = tEl.a 
     b = tEl.b 
     c = tEl.c 

     print("thread {:} elnumber {:}".format(tid, tEl.elNumber)) 

    #nothing is done here 

    with gil: 
     print("thread {:} elnumber {:}".format(tid, tEl.elNumber)) 

    # some other computations based on a, b and c here ... 

我期望的輸出是這樣的:

thread 0 elnumber 1 
thread 1 elnumber 2 
thread 2 elnumber 3 
thread 3 elnumber 4 
thread 0 elnumber 1 
thread 1 elnumber 2 
thread 2 elnumber 3 
thread 3 elnumber 4 

,但我得到:

thread 1 elnumber 1 
thread 0 elnumber 3 
thread 3 elnumber 2 
thread 2 elnumber 4 
thread 3 elnumber 4 
thread 1 elnumber 2 
thread 0 elnumber 4 
thread 2 elnumber 4 

所以,不知何故線程局部變量tEl在線程中被覆蓋?我究竟做錯了什麼 ?謝謝!

+0

你說得對。它似乎並沒有讓'tEl'線程局部(看看生成的C文件,並搜索'lastprivate'來檢查)。如果您將其更改爲基本類型(如'double'),它可以工作,但似乎不適用於Cython對象類型。我不知道一個明顯的解決方案,但它可能是值得在github上提供一個錯誤 – DavidW

回答

2

看起來Cython故意選擇從線程局部變量列表中排除任何Python變量(包括Cython cdef class es)。 Code

我懷疑這是故意避免引用計數問題 - 他們需要在循環結束時刪除所有線程局部變量的引用計數(這不會是一個無法克服的問題,但可能會是一個很大的變化)。因此我認爲這不太可能得到解決,但文檔更新可能會有所幫助。

解決的辦法是你的循環體refactorise成函數,其中每一個變量結束了有效的「本地」的功能,因此,它不是一個問題:

cdef f(CythonElement tEl): 
    cdef int tid 
    with nogil: 
     tid = threadid() 
     with gil: 
      print("thread {:} elnumber {:}".format(tid, tEl.elNumber)) 

     with gil: 
      print("thread {:} elnumber {:}".format(tid, tEl.elNumber)) 

    # I've trimmed the function a bit for the sake of being testable 

# then for the loop: 
for n in prange(nElements, schedule='dynamic', nogil=True): 
    with gil: 
     f() 
+0

好吧,我明白了。不幸的是,似乎cdef擴展的並行調用是不可能的。現在重構純cdef函數調用是不現實的。在你的例子中,f()是否聲明沒有nogil標誌,因此,是不是並行執行? – macmallow

+0

它需要gil在開始和結束時如果調用refEount tEl。它可以釋放內部的gil,並且該位將並行運行。它不應該比你的問題中的代碼更糟糕(顯然在這個玩具的例子中,幾乎沒有「無gil」的工作)。 – DavidW

0

Cython提供基於線程的並行性。不保證執行線程的順序,因此thread的無序值。

如果您希望tEl對該線程是私有的,則不應該全局定義它。嘗試在prange內移動cdef CythonElement tEl。見http://cython-devel.python.narkive.com/atEB3yrQ/openmp-thread-private-variable-not-recognized-bug-report-discussion(私有變量的一部分)。

+0

感謝您的快速響應。然而,編譯給我: >>> cdef聲明不允許在這裏<<<(這個問題也在發佈的鏈接中提到?) – macmallow

+0

好吧,我有一些標籤打開,我找不到我的地方看到cdef移動了。重新讀你的例子,我沒有看到一個函數。通常,將cython文件編譯爲模塊,並調用結果函數。代碼是否在完整文件之上? –

+0

它實際上是在鏈接。搜索「cdef w = Worker(n)#block-scoped cdef」 –