【问题标题】:PyCuda / Multiprocessing Issue on OS X 10.8OS X 10.8 上的 PyCuda / 多处理问题
【发布时间】:2013-02-05 23:43:16
【问题描述】:

我正在做一个项目,我将计算任务分配给多个 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

我没有在我的 Mac 上分叉新进程的情况下运行 PyCuda 脚本没有问题。我只有在生成新进程时才会遇到这个问题。

以前有人遇到过这个问题吗?

【问题讨论】:

  • 我怀疑这与 OS X 有一大堆核心框架在fork 之后无法使用有关,而且 PyCuda 或 CUDA 本身都依赖其中一个......
  • 我实际上也认为是这样。有没有办法解决?真的很烦人。
  • 如果是这种情况,最简单的解决方法是执行一个新的 Python 解释器,而不是继续使用分叉的解释器。在某个地方有一个补丁版本的multiprocessing 可以做到这一点。 (它可能有一天会作为一个选项添加到主干,但它永远不会成为默认设置,因为这会使 OS X multiprocessing 更像 Windows,而不是像 POSIX。)如果你想,你找不到它或自己弄清楚如何做(实际上很简单),我可以挖掘它。
  • 没关系,找到了。特定于 Mac 的问题已作为错误提交,该错误已作为 #8713 的副本关闭(上面写着“Linux”,但实际上与所有 POSIX 平台有关)。如果您从源代码中复制multiprocessing.py,应用补丁,将其重命名为其他名称,然后在其他任何内容之前调用mymultiprocessing.forking_disable(),它应该可以工作。 (2.7的补丁可能需要稍微按摩一下,但应该不会太难。)
  • CudaWorker 进程对 GPU 的限制肯定比对 CPU 的限制更多,因此线程解决方案可能会起作用。如果我没记错的话,自从 PyCuda 0.9 CUDA 内核调用释放了 GIL,所以使用线程也不应该导致任何问题。

标签: python cuda multiprocessing osx-mountain-lion pycuda


【解决方案1】:

根据我的经验,这实际上只是一个有根据的猜测,但我怀疑 CUDA(或可能是 PyCuda)的 OS X 实现依赖于一些在fork 之后无法安全使用的 API,而 linux 实现没有。* 由于multiprocessing 的 POSIX 实现使用 fork 没有 exec 创建子进程,这可以解释为什么它在 OS X 而不是 linux 上失败。 (在 Windows 上,没有fork,只有spawn 等价物,所以这不是问题。)

最简单的解决方案是删除multiprocessing。如果 CUDA 和 PyCUDA 是线程安全的(我不知道它们是否是线程安全的),并且您的代码不受 CPU 限制(只是受 GPU 限制),那么您可以直接使用 threading.Thread 代替 @987654330 @ 并完成它。或者,您可以考虑提供与multiprocessing 类似的 API 的其他并行处理库之一。 (有少数人使用pp只是因为它总是execs...)

但是,很容易将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/在你做任何其他事情之前在最新的补丁中调用任何模式。


* OP 说他怀疑同样的事情,所以我可能不需要向他解释这一点,但对于未来的读者:这不是关于达尔文和其他 Unix 之间的区别,而是关于苹果发布了一个事实许多非 Unix-y 中级库,如 CoreFoundation.framework、Accelerate.framework 等,它们使用 unsafe-after-fork 功能(或者只是断言它们在 fork 之后没有被使用,因为 Apple 不想要在他们可以说“从 10.X 开始,Foo.framework 在 fork 之后是安全的”之前进行严格的测试。此外,如果您比较 OS X 和 linux 处理图形和其他硬件的方式,OS X 中的每个进程用户空间中都存在更多的中级。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-03
    • 1970-01-01
    • 2013-03-03
    • 2012-11-08
    • 2013-01-06
    • 2013-07-19
    • 2013-07-12
    相关资源
    最近更新 更多