将大型ndarray快速放入多处理.Queue

时间:2015-01-20 22:19:59

标签: python multiprocessing

尝试将大ndarray放入Queue的{​​{1}}时,遇到以下问题:

首先,这是代码:

Process
  1. 如果我使用import numpy import multiprocessing from ctypes import c_bool import time def run(acquisition_running, data_queue): while acquisition_running.value: length = 65536 data = numpy.ndarray(length, dtype='float') data_queue.put(data) time.sleep(0.1) if __name__ == '__main__': acquisition_running = multiprocessing.Value(c_bool) data_queue = multiprocessing.Queue() process = multiprocessing.Process( target=run, args=(acquisition_running, data_queue)) acquisition_running.value = True process.start() time.sleep(1) acquisition_running.value = False process.join() print('Finished') number_items = 0 while not data_queue.empty(): data_item = data_queue.get() number_items += 1 print(number_items) 左右,一切正常。我通过队列传输了9个项目。

  2. 如果我增加到length=10,则在我的计算机上length=1000阻止,但功能process.join()已经完成。我可以使用run()对该行进行评论,并且会看到,队列中只放置了2个项目,因此显然将数据放入队列的速度非常慢。

  3. 我的计划实际上是传输4个ndarray,每个长度为65536.对于process.join(),这个工作非常快(<1ms)。有没有办法提高流程传输数据的速度?

    我在Windows机器上使用Python 3.4,但在Linux上使用Python 3.4我会得到相同的行为。

4 个答案:

答案 0 :(得分:7)

&#34;有没有办法提高流程传输数据的速度?&#34;

当然,给出正确的解决问题。目前,您只需填充缓冲区而不会同时清空缓冲区。 恭喜,您刚刚为自己构建了一个所谓的死锁相应的引用from the documentation是:

  

请记住,将项目放入队列的进程将等待   在终止之前,直到所有缓冲的项目都由   “馈线”线程到底层管道。

但是,让我们慢慢接近这个。首先,&#34;速度&#34;不是你的问题!我知道您只是在尝试使用Python multiprocessing。阅读代码时最重要的见解是父母和孩子之间的沟通流程,特别是事件处理并没有多大意义。如果你有一个你想要解决的现实问题,你绝对无法解决它这个方式。如果你没有现实问题,那么在开始编写代码之前,首先需要提出一个好问题;-)。最后,您需要了解操作系统为进程间通信提供的通信原语。

您正在观察的内容的解释:

您的子进程生成大约10 * length * size(float)个字节的数据(考虑到您的子进程可以执行大约10次迭代,而您的父进程在将acquisition_running设置为False之前大约休息1秒)。当您的父进程休眠时,子进程将命名的数据量放入队列中。您需要了解队列是一个复杂的构造。你不需要了解它的每一点。但有一件事确实非常重要:进程间通信的队列显然使用了父进程和子进程之间的某种缓冲区*。缓冲区通常具有有限的尺寸。您正在从子中写入此缓冲区而没有同时从父级中读取它。也就是说,缓冲内容在父母刚睡觉时稳定增长。通过增加length,您会遇到队列缓冲区已满并且子进程无法再写入它的情况。但是,子进程在写入所有数据之前不能终止。同时,父进程等待子进程终止。

你知道吗?一个实体等待另一个实体。父母等待孩子终止,孩子等待父母留出一些空间。这种情况称为死锁。它无法自行解决。

关于细节,缓冲区情况比上述情况稍微复杂一些。您的子进程产生了一个额外的线程,它试图通过管道将缓冲的数据推送到父级。实际上,这个管道的缓冲区是限制实体。它由操作系统定义,至少在Linux上,通常不大于65536字节。

换句话说,基本部分是:在孩子完成尝试写之前,父从管 管。在使用管道的每个有意义的场景中,读取写入以相当同时的方式发生,以便一个进程可以快速响应由另一个过程。你正在做相反的事情:你让你的父母睡觉,因此让它对孩子的输入不敏感,导致死锁情况。

(*)&#34; 当一个进程首先将一个项目放入队列时,启动一个进给者线程,它将对象从缓冲区传输到管道&#34;,来自{{3 }}

答案 1 :(得分:1)

如果你有非常大的数组,你可能只想传递他们的pickle状态 - 或者更好的选择可能是使用multiprocessing.Arraymultiprocessing.sharedctypes.RawArray来创建一个共享内存数组(对于后者,见http://briansimulator.org/sharing-numpy-arrays-between-processes/)。您必须担心冲突,因为您将拥有一个不受GIL约束的阵列 - 并且需要锁定。但是,您只需要发送数组索引来访问共享数组数据。

答案 2 :(得分:0)

与JPG的优秀答案相结合,您可以做的一件事就是在每个进程之间卸载Queue。

所以这样做:

process.start()
data_item = data_queue.get()
process.join()

虽然这并没有完全复制代码中的行为(数据计数的数量),但你明白了;)

答案 3 :(得分:-2)

将数组/列表转换为str(your_array)

q.put(str(your_array))