多处理事件使我的代码变慢

时间:2015-05-27 02:40:36

标签: python multiprocessing finance python-multiprocessing

我有一个主流程正在吃来自许多不同市场的数据。它对消息进行一些初步处理,然后将其传递到多处理队列(每个独特的市场都有自己的专用进程,在队列的另一端称为Parse)。然后主要流程为所涉及的特定市场调用mp.Event.set()

Parse内是对mp.Event.wait()的调用,除非有数据被送入队列,否则会暂停进程。在Parse结束时,它调用mp.Event.clear().我这样做是因为我使用while True循环来捕获数据到队列的那一刻。如果我不暂停Parse,它将使用100%的CPU并且我没有足够的内核(更不用说它非常浪费了)。

今天晚上,我意识到Parse花了很长时间才能跑,从.3秒到18秒。市场数据消息可以每12毫秒发出一次,所以显然这是不可行的。除Parse外,mp.Event.wait()的每个方面都非常快。此调用几乎占运行时间的100%。

我将所有mp.Event个对象存储在配置文件中定义的字典中。我担心发生两件事之一:

  1. 设置和清除事件的每个实例都会阻塞所有其他事件,其方式类似于mp.Manager如何处理共享对象。

  2. mp.Event速度很慢,其状态需要很长时间才能跨进程传播......

  3. 我正在考虑通过使用zmq(ZeroMQ)而不是mp.Queue来管理数据来解决这个问题,但在我设置之前,我想问聪明的人。

    我在做一些明显错误的事吗?有没有办法加快mp.Event标记?

    修改

    在回应评论时,这是一个例子:

    config.py文件中,我像这样定义字典:

    E,Q={},{}
    for m in all_markets:
        E[m] = mp.Event()
        Q[m] = mp.Queue()
    

    然后在读取数据的主进程中,我调用sort,看起来像这样:

    def sort(message, m):
        if message satisfies condition1:
            define some args
            Q[m].put(message, *args)
            E[m].set()
        if message satisfies condition2:
            #basically the same
    

    然后最后在Parse,这是在程序启动时启动的:

    def Parse(message,m,Q,E):
        while True:
            E[m].wait()
            message = Q[m].get()
            #do a bunch of processing on the message
            #put the results in some other queues
            E[m].clear()
    

    EDIT2

    生成过程并像这样开始:

    def mitosis():
        mp.Process(target=main).start()
    
    def pstart(m,func,**kwargs):
        if func=='parser':
            p = mp.Process(target=parser, args=(m, Q, E, *args) )
            p.start()
    
    def main():
        PROCS={}
        for m in all_markets:
            for procs in proclist:
            PROCS[(m,proc)] = pstart(m,proc,**kwargs)
    

1 个答案:

答案 0 :(得分:2)

我认为您的问题是您的Event代码已损坏。

想象一下这种情况:

  • 主要流程为sort调用m
  • sort来电Q[m].putE[m].set
  • Parse醒来,Q[m].get,然后开始处理。
  • 主要流程再次针对相同的sort调用m
  • sort来电Q[m].putE[m].set
  • Parse完成处理第一条消息,调用E[m].clear

现在Parse正在等待再次设置Event。这可能不会发生很长一段时间。并且,即使它很快发生,它仍然不会赶上;它只为每个Q[m].get执行一次Event.wait

所以,你最终得到的是Parse看起来越来越远。当你试图对其进行分析以找出原因时,你会看到它花费所有时间等待E[m].wait。但这不是因为E[m].wait很慢,而是因为事件触发器丢失了。

这不是唯一的竞争条件,它只是最明显的竞争条件。

一般问题是您不能以这种方式使用事件对象。通常情况下,您可以使用Condition替代,或一次性触发和自动重置Event来解决此问题,并在每个Q[m].get(block=False)后循环Event

但实际上,首先没有必要这样做。如果你只是完全移除Event,那么当Parse调用Q[m].get时,会阻塞,直到那里有东西为止。因此,当sort调用Q[m].put时,会唤醒Parse,并且没有其他任何需要。

事实上,Queue的重点在于它本身就是自我同步的。如果您不想这样做,请使用Pipe,然后您可以使用Condition进行信令。但在简单的情况下,这只是Queue的效率较低的版本。