2013-02-05 24 views
2

我正在開發一個項目,將計算任務分配給多個python進程,每個進程與其自己的CUDA設備相關聯。OS X上的PyCuda /多處理問題10.8

當產卵的子過程,我用下面的代碼:

import pycuda.driver as cuda 

class ComputeServer(object): 
    def _init_workers(self): 
     self.workers = [] 
     cuda.init() 
     for device_id in range(cuda.Device.count()): 
      print "initializing device {}".format(device_id) 
      worker = CudaWorker(device_id) 
      worker.start() 
      self.workers.append(worker) 

的CudaWorker在另一個文件中定義如下:

from multiprocessing import Process 
import pycuda.driver as cuda 

class CudaWorker(Process): 
    def __init__(self, device_id): 
     Process.__init__(self) 
     self.device_id = device_id 

    def run(self): 
     self._init_cuda_context() 
     while True: 
      # process requests here 

    def _init_cuda_context(self): 
     # the following line fails 
     cuda.init() 
     device = cuda.Device(self.device_id) 
     self.cuda_context = device.make_context() 

當我運行在Windows 7或Linux的代碼,我沒有問題。當OSX 10.8.2,Cuda的5.0和PyCuda 2012.1上運行我的MacBook Pro的代碼我收到以下錯誤:

Process CudaWorker-1: 
Traceback (most recent call last): 
    File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap 
    self.run() 
    File "/Users/tombnorwood/pymodules/computeserver/worker.py", line 32, in run 
    self._init_cuda_context() 
    File "/Users/tombnorwood/pymodules/computeserver/worker.py", line 38, in _init_cuda_context 
    cuda.init() 
RuntimeError: cuInit failed: no device 

我有運行PyCuda腳本,而無需在我的Mac分叉新工藝沒有問題。我只在產生一個新的進程時纔會遇到這個問題。

有沒有人遇到過這個問題?

+0

我懷疑這與以下事實有關: OS X有一大堆核心框架,不能在'fork'之後使用,並且PyCuda或CUDA本身都依賴於其中的一個... – abarnert

+0

我實際上也認爲這種情況也是如此。有沒有辦法解決?這真的很煩人。 – tnorwood

+0

如果是這樣,最簡單的解決方法是執行一個新的Python解釋器,而不是繼續使用分叉的解釋器。有一個補丁版本的'multiprocessing'圍繞某處這麼做。 (它可能有一天會被添加到主幹中作爲一個選項,但它永遠不會是默認的,因爲這會使得OS X'multiprocessing'比POSIX更像Windows。)如果你想要,而且你找不到它或者弄清楚如何自己做(實際上這很簡單),我可以爲它挖掘它。 – abarnert

回答

2

這實際上只是一個基於我的經驗的教育猜測,但我懷疑CUDA(或可能PyCuda)的OS X實現依賴於某些在fork之後無法安全使用的API,而linux實現不是。*由於POSIX執行multiprocessing使用fork而沒有exec來創建子進程,這可以解釋爲什麼它在OS X而不是Linux上失敗。 (和Windows,還有沒有fork,只是spawn相等的,所以這不是一個問題。)

最簡單的解決辦法是放棄multiprocessing。如果CUDA和PyCUDA是線程安全的(我不知道它們是否是),並且您的代碼不受CPU限制(僅限於GPU),您可以在threading.Thread中代替multiprocessing.Process並完成用它。或者您可以考慮提供類似於multiprocessing的API的其他並行處理庫之一。 (還有誰使用pp不僅是因爲它總是exec個幾個人......)

然而,這是很容易破解了multiprocessingexec/spawn一個新的Python解釋器,然後盡一切Windows的風格,而不是POSIX風格。 (獲取情況下正確的是困難的,但得到一個具體的使用權的情況下很容易。)

或者,如果你看一下bug #8713,還有對一般使這項工作正在正確的做了一些工作。還有一些工作補丁。那些補丁是3.3,而不是2.7,所以你可能需要一些按摩,但它不應該太多。因此,只需cp $MY_PYTHON_LIB/multiprocessing.py $MY_PROJECT_DIR/mymultiprocessing.py,對其進行修補,使用mymultiprocessing代替multiprocessing,然後添加適當的調用以選擇spawn/fork + exec /在最新修補程序中調用哪種模式,然後再執行其他任何操作。


*的OP說,他懷疑同樣的事情,所以我可能不需要解釋給他,但對於未來的讀者:這不是達爾文和其他的Unix之間大約有差別,但有關的事實Apple發佈了許多非Unix-y中級庫,例如CoreFoundation.framework,Accelerate.framework等,它們使用叉後不安全功能(或者斷言它們沒有在fork之後使用,因爲Apple不希望進行嚴格的測試,在他們可以說「從10.X開始,Foo.framework在fork之後是安全的」之前,這將是必要的)。另外,如果您比較OS X和Linux處理圖形和其他硬件的方式,則在OS X中會有更多中級每個進程內用戶空間。