Python多进程numpy.linalg.pinv引起segfault问题

3

我使用Python的多进程包编写了一个函数,试图提高代码的速度。

from arch.univariate import ARX, GARCH
from multiprocessing import Process
import multiprocessing
import time

def batch_learning(X, lag_array=None):
    """
    X is a time series array
    lag_array contains all possible lag numbers
    """
    # init a queue used for triggering different processes
    queue = multiprocessing.JoinableQueue()
    data = multiprocessing.Queue()

    # a worker called ARX_fit triggered by queue.get()
    def ARX_fit(queue):
        while True:
            q = queue.get()
            q.volatility = GARCH()
            print "Starting to fit lags %s" %str(q.lags.size/2)
            try:
                q_res=q.fit(update_freq=500)
            except:
                print "Error:...."
            print "finished lags %s" %str(q.lags.size/2)
            queue.task_done()
    # init four processes
    for i in range(4):
        process_i = Process(target=ARX_fit, name="Process_%s"%str(i),   args=(queue,))
        process_i.start()
    # put ARX model objects into queue continuously
    for num in lag_array:
        queue.put(ARX(X, lags=num))

    # sync processes here
    queue.join()   

    return

调用函数后:

batch_learning(a, lag_array=range(1,10))

然而,它在中途卡住了,我收到了以下打印输出消息:
Starting to fit lags 1
Starting to fit lags 3
Starting to fit lags 2
Starting to fit lags 4
finished lags 1
finished lags 2
Starting to fit lags 5
finished lags 3
Starting to fit lags 6
Starting to fit lags 7
finished lags 4
Starting to fit lags 8
finished lags 6
finished lags 5
Starting to fit lags 9

我在我的Mac OS El Captain上运行程序,但是它一直没有任何输出。然后,在使用PyCharm调试模式和感谢Tim Peters的建议后,我成功地发现这些进程实际上异常退出了。在调试模式下,我可以确定问题实际上是由arch库中numpy.linalg.pinv()中使用的svd函数引起的。那么,我的问题是:为什么?它可以使用单进程for循环工作,但无法使用2个或更多进程工作。我不知道该如何解决这个问题。这是一个numpy的bug吗?有人能在这方面帮助我吗?


奇怪的是,如果我删除 try: q_res=q.fit(update_freq=500) except: print "Error:....",它就可以正常工作。我猜测fit函数可能有问题? - Gauss Lee
你使用的是什么平台/操作系统? OS X Accelerate框架在多进程方面存在问题,表现方式类似。 - aganders3
@aganders3:我正在使用Mac OS El Captain。你知道如何解决这个问题吗? - Gauss Lee
据我所知,目前还没有解决方案,但有一些变通方法。这是我们实验室的主要困扰。请参考此问题以获取更多解释:https://dev59.com/4Gkw5IYBdhLWcg3wdKQv - aganders3
1
在我的“答案”评论中建议尝试使用Python 3(3.4或更高版本)和multiprocessing spawn启动方法。或者在Windows上使用任何版本的Python。这些都将从方程式中取出fork()。Python本身忍受着无数痛苦,以使threading.Thread线程与fork()协调一致,但无法做任何事情使其他软件的线程变得合理。 - Tim Peters
2个回答

3
我必须自己回答这个问题并提供我的解决方案。我已经解决了这个问题,感谢@Tim Peters和@aganders的帮助。
当你在Mac OS上使用numpy/scipy库时,多进程通常会挂起,因为苹果操作系统中使用了加速框架,它是OpenBlas numpy的替代品。简单地说,为了解决类似的问题,您需要按照以下步骤进行:
1. 卸载numpy和scipy(scipy需要与适当版本的numpy匹配) 2. 按照此link上的过程重新构建numpy,并使用Openblas。 3. 重新安装scipy并测试您的代码以查看是否正常工作。
在Mac OS上测试多进程代码时,有一些提示,当您运行代码时,最好设置一个环境变量来运行您的代码:
OPENBLAS_NUM_THREADS=1 python import_test.py

这样做的原因是OpenBlas默认为每个核心创建2个线程运行,即使您设置了4个进程,也会有8个线程运行(每个核心2个)。这会增加一些线程切换的开销。我测试了OPENBLAS_NUM_THREADS = 1配置,限制每个核心上每个进程只有1个线程,比默认设置更快。

1
这里提供的信息不够充分,而且代码缩进有误,很难猜测您实际在做什么。就我所能猜测的情况来看,如果操作系统以一种未引发Python异常的方式终止了进程,则会出现您看到的情况。
可以尝试一件事:首先创建一个名为ps的列表,其中包含您的四个process_i对象。然后在queue.join()之前添加以下内容:
while ps:
    new_ps = []
    for p in ps:
        if p.is_alive():
            new_ps.append(p)
        else:
            print("*********", p.name, "exited with", p.exitcode)
    ps = new_ps
    time.sleep(1)

每秒钟,此程序会遍历工作进程列表,检查是否有任何工作进程(意外地!)已经停止。如果有一个或多个进程停止了,它会显示进程名称(您已经提供)和进程退出代码(由您的操作系统提供)。如果触发了这个条件,那将是一个很重要的线索。
如果没有进程停止,那么我们就需要想知道为什么。
q_res=q.fit(update_freq=500)

"simply" 对于某些 q 状态需要很长时间。

抱歉出现了缩进问题,我已经修复了。现在应该可以读取了。我的目标是创建一个多进程函数,使用不同的参数训练一种架构模型很多次。q.fit() 函数是我需要并行处理的函数。它在完成6个任务后卡住了,我不知道为什么。你可以从输出中看到这一点。当lags=7时它卡住了,而且当lags>=7时它永远无法完成任务。 - Gauss Lee
嗨,Tim。感谢您的回复。我尝试将您的片段添加到我的代码中,但进程意外终止了。输出为 ('*********', 'Process_0', 'exited with', 1) ('*********', 'Process_1', 'exited with', -11) ('*********', 'Process_2', 'exited with', -11) ('*********', 'Process_3', 'exited with', -11)。我对多进程编程不是很熟悉。我想知道进程被终止的原因是什么。 - Gauss Lee
这张票有一些额外的评论。我在Pycharm中尝试了代码,它告诉我python quits unexpected with _u_math_linalg.so crash (segfault)。然后我开始调试错误所在的位置。我发现问题非常深入。然后是由于q引起的,它是基于numpy构建的arch库中的ARX对象。更深入地说,确定了np.linalg.pinv函数内部的numpy函数svd(a, 0)。当它达到lag num = 8时,似乎svd崩溃导致进程退出。我跟踪了sav(a,0)内部的a。看来一个不适当的a导致了崩溃。 - Gauss Lee
然而,我仍然无法弄清楚为什么单个for循环可以毫无问题地运行它,但使用多进程会导致崩溃。有一个疑问,即np.linalg.pinv不是线程安全的。我已经看到一些由相关np.linalg函数引起的多进程崩溃。这是另一个bug吗?请帮忙! - Gauss Lee
是的,在Linux系统中,“-11”表示进程被段错误杀死。退出状态“1”没有普遍适用的含义,这取决于您运行的具体软件。我对arch没有任何经验。 - Tim Peters
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接