在Python中多进程C++代码时未见任何加速?
我有一些Python代码,它依赖于一段C++代码,这段代码主要用于进行大规模的树搜索。我需要在一个循环中运行这个树搜索n次,由于每次运行都要花费几秒钟,我觉得这是一个明显的地方可以用Python的多进程来加速。我的多进程设置比较标准:
with Pool() as pool:
probs = pool.starmap(self._probs_multi, zip(self.features, self.scales, repeat(labels), repeat(nr_classes)))
我发现当我运行它时,n个核心都在100%负载运转(当n小于我的核心数量时),而不是顺序运行时只有1个核心在100%负载。不过,整个循环的总运行时间还是和之前一样!
现在我用类似下面的方式加载C++库:
import ctypes
import numpy.ctypeslib as ctl
libfile = os.path.dirname(__file__) + '/km_dict_lib.so'
lib = ctypes.cdll.LoadLibrary(libfile)
py_km_tree_multi = lib.build_km_tree_multi
# say which inputs the function expects
py_km_tree_multi.argtypes = [ctl.ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"),
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,
ctypes.c_bool,
ctl.ndpointer(ctypes.c_double, flags="C_CONTIGUOUS")]
py_km_tree_multi(image, rows, cols, channels, n_im,
self.patch_size, self.number_layers, self.branching_factor,
number_training_patches, self.normalization, self.tree)
我能想到两个原因,为什么顺序运行和多进程运行的速度差不多。第一个原因是这个任务其实不是受CPU限制,而是受内存限制。我猜对于树搜索来说可能会是这样,我对树搜索不是很熟悉,但如果真是这样,那所有n个核心都在100%负载就让我觉得奇怪,这似乎说明应该是受CPU限制的?
另一个可能是,当Python脚本调用外部的C++函数时,多进程可能没有真正起作用,或者我对这个问题的简单处理方法不够好。
有没有人能给我一些建议,帮我找出问题所在,看看我该如何继续?
1 个回答
你遇到了GIL(全局解释器锁)的问题。大多数Python的实现都有这个全局锁,所以解释器基本上是顺序执行的。根据我的了解,想要在Python中实现并行处理,唯一的方法就是用它来调度其他可以并行运行的任务。在你的情况下,zip调用会被等待完成,然后循环才会继续到下一个迭代。你可能需要对你的代码做一些调整,加一个小的包装器来调度原始任务,然后快速返回到循环中。祝好。
好的。我其实在查看解释器的源代码。确实,GIL并不是一个全局互斥锁,它只是一个原子类型的uint8。但它本来可以是的,对吧?那样的话,线程之间甚至跨进程都会有竞争。好吧,事实并不是这样。
关于并行处理和GIL没有关系。抱歉,如果代码里有回调的话,那就有关系了。这些回调会返回到主进程执行。所以事情并没有那么简单明了。好的,提问者似乎没有明显的回调。或者说,他间接上有吗?
不过还有另一个可能的问题。不是GIL的问题,而是可能是用来保护共享对象访问的信号量。
或者你觉得我又错了,你想把我投票下去,赶出这个论坛吗?