导入PySide后,Python子进程在numpy dot时崩溃
我在我的电脑上遇到了一个很奇怪的问题,当我使用 Python 的多进程池(multiprocessing Pool)并且导入了 numpy 和 PySide 时,程序会卡住。这是我见过的最复杂的错误之一:) 下面的代码:
import numpy as np
import PySide
def hang():
import multiprocessing
pool = multiprocessing.Pool(processes = 1)
pool.map(f, [None])
def f(ignore):
print('before dot..')
np.dot(np.zeros((128, 1)), np.zeros((1, 32)))
print('after dot.')
if __name__ == "__main__":
hang()
print('success!')
只打印出 'before dot..',但实际上应该打印出
before dot..
after dot.
success!
我不是 gdb 的专家,但看起来 gdb 显示进程在 'np.dot' 这一行退出(或崩溃):
[Inferior 1 (process 2884) exited normally]
我可以做一些神奇的修改来防止程序卡住:
- 如果你把传入 'dot' 的数组的形状从 128 减少到 127
- (!) 如果你把传入 'dot' 的数组的形状从 128 增加到 256
- 如果你不使用多进程,而是直接运行函数 'f'
- (!!!) 如果你注释掉 PySide 的导入,因为在代码中并没有用到它
任何帮助都非常感谢!
包的版本:
numpy=1.8.1 或 1.7.1,PySide=1.2.1 或 1.2.2
Python 版本:
Python 2.7.5(默认,2013年9月12日,21:33:34) [GCC 4.2.1 兼容 Apple LLVM 5.0 (clang-500.0.68)] 在 darwin 上
或者
Python 2.7.6(默认,2014年4月9日,11:48:52) [GCC 4.2.1 兼容 Apple LLVM 5.1 (clang-503.0.38)] 在 darwin 上
注意:在寻找信息的过程中,我稍微简化了原始代码和问题。但这里有一堆更新,以便为可能遇到这个错误的其他人保留历史(例如,我最开始是从 matplotlib 开始的,而不是 pyside)
更新:我把 pylab 的导入缩小到只导入带有 pyside 后端的 matplotlib,并更新了代码以运行。
更新:我正在修改帖子,只导入 PySide,而不是:
import matplotlib
matplotlib.use('qt4agg')
matplotlib.rcParams['backend.qt4']='PySide'
import matplotlib.pyplot
更新:初步统计显示这是一个仅限 Mac 的问题。3 个人在 Ubuntu 上运行正常,2 个人在 Mac 上卡住。
更新:在 dot 操作之前打印 print(os.getpid()) 给我一个我在 'top' 中看不到的进程 ID,这显然意味着它崩溃了,而多进程在等待一个死掉的进程。因此我无法将调试器附加到它上面。我相应地编辑了主要问题。
3 个回答
我遇到了完全一样的问题。当子进程使用numpy.dot时,出现了死锁。但是当我把矩阵的大小减小时,它就能正常运行。所以我没有在一个包含156000个浮点数的矩阵上做点积,而是进行了3次52000个浮点数的点积,然后把结果拼接在一起。我不太确定最大限制是什么,也不清楚这是否和子进程的数量、可用内存或其他因素有关。不过,如果能通过反复试验找出不会死锁的最大矩阵,那么下面的代码应该会有帮助。
def get_batch(X, update_iter, batchsize):
curr_ptr = update_iter*batchsize
if X.shape[0] - curr_ptr <= batchsize :
X_i = X[curr_ptr:, :]
else:
X_i = X[curr_ptr:curr_ptr+batchsize, :]
return X_i
def batch_dot(X, w, batchsize):
y = np.zeros((1,))
num_batches = X.shape[0]/batchsize
if X.shape[0]%batchsize != 0:
num_batches += 1
for batch_iter in range(0, num_batches):
X_batch = get_batch(X, batch_iter, batchsize)
y_batch = X_batch.dot(w)
y = np.hstack((y, y_batch))
return y[1:]
我觉得这是多进程模块的问题。
你可以试试下面这个方法。
import numpy as np
import PySide
def hang():
import multiprocessing.dummy as multiprocessing
pool = multiprocessing.Pool(processes = 1)
pool.map(f, [None])
def f(ignore):
print('before dot..')
np.dot(np.zeros((128, 1)), np.zeros((1, 32)))
print('after dot.')
if __name__ == "__main__":
hang()
print('success!')
这是一个关于一些BLAS库的普遍问题,这些库是numpy在执行dot
操作时使用的。
Apple的Accelerate库和用GNU Openmp构建的OpenBlas在进行多进程处理时,父进程和子进程之间的使用是有问题的,它们会导致死锁。
这个问题无法通过numpy来解决,但有三种解决方法:
- 使用netlib BLAS、ATLAS,或者基于pthread的git master版本的OpenBlas(2.8.0版本不适用)
- 使用Python 3.4及其新的多进程
spawn
或forkserver
启动方法 - 使用线程而不是多进程,numpy在大多数耗时的操作中会释放全局解释器锁(GIL),这样在普通的桌面电脑上你可以获得不错的线程速度提升