我使用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吗?有人可以帮我一点吗?
答案 0 :(得分:3)
我必须自己回答这个问题并提供我的解决方案。由于@Tim Peters和@aganders的帮助,我已经解决了这个问题。
当您在Mac OS上使用numpy / scipy库时,多处理通常会挂起,因为Apple OS中使用的Accelerate Framework是OpenBlas numpy的替代品。简单地说,为了解决类似的问题,你必须做到如下:
按照此link上的步骤使用Openblas重建numpy。
重新安装scipy并测试您的代码以查看它是否有效。
有些人会在Mac OS上测试你的多处理代码,当你运行你的代码时,最好设置一个env变量来运行你的代码:
OPENBLAS_NUM_THREADS=1 python import_test.py
这样做的原因是OpenBlas默认为每个核心创建2个线程来运行,在这种情况下,即使你设置了4个进程,也有8个线程在运行(每个核心2个)。这为线程切换创造了一些开销。我测试了OPENBLAS_NUM_THREADS = 1 config以限制每个核心上每个进程的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)
对某些q
州而言,“简单地”需要很长时间。