我正在开展一个项目,我将计算任务分配给多个python进程,每个进程都与自己的CUDA设备相关联。
当产生子进程时,我使用以下代码:
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 :(得分:2)
根据我的经验,这实际上只是一个有根据的猜测,但我怀疑CUDA(或可能是PyCuda)的OS X实现依赖于fork
之后无法安全使用的某些API,而linux实现没有。*由于multiprocessing
的POSIX实现使用fork
而没有exec
来创建子进程,这可以解释为什么它在OS X上失败但在linux上失败。 (在Windows上, 没有fork
,只有spawn
等效,所以这不是问题。)
最简单的解决方案是放弃multiprocessing
。如果CUDA和PyCUDA是线程安全的(我不知道它们是否是),并且你的代码不受CPU限制(只是受GPU限制),你可能只需要放入threading.Thread
代替multiprocessing.Process
并完成它。或者您可以考虑为multiprocessing
提供类似API的其他并行处理库之一。 (有些人仅使用pp
,因为它总是exec
s ...)
然而,将multiprocessing
升级到exec
/ 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 /在最新补丁中调用模式之前你做了别的事。