我正在编写一个动画图像数据的脚本。我有许多大型图像立方体(3D阵列)。对于其中的每一个,我逐步浏览每个立方体中的帧,一旦我接近它的末尾,我加载下一个立方体并继续。由于每个立方体的大尺寸,存在显着的加载时间(~5s)。我希望动画能够无缝地在多维数据集之间进行转换(同时也节省了内存),所以我正在错开加载过程。我在解决方案方面取得了一些进展,但仍存在一些问题。
下面的代码加载每个数据立方体,将其拆分为框架并将它们放入multiprocessing.Queue
。一旦队列中的帧数低于某个阈值,就会触发下一个加载进程,加载另一个多维数据集并将其解压缩到队列中。
查看以下代码:
import numpy as np
import multiprocessing as mp
import logging
logger = mp.log_to_stderr(logging.INFO)
import time
def data_loader(event, queue, **kw):
'''loads data from 3D image cube'''
event.wait() #wait for trigger before loading
logger.info( 'Loading data' )
time.sleep(3) #pretend to take long to load the data
n = 100
data = np.ones((n,20,20))*np.arange(n)[:,None,None] #imaginary 3D image cube (increasing numbers so that we can track the data ordering)
logger.info( 'Adding data to queue' )
for d in data:
queue.put(d)
logger.info( 'Done adding to queue!' )
def queue_monitor(queue, triggers, threshold=50, interval=5):
'''
Triggers the load events once the number of data in the queue falls below
threshold, then doesn't trigger again until the interval has passed.
Note: interval should be larger than data load time.
'''
while len(triggers):
if queue.qsize() < threshold:
logger.info( 'Triggering next load' )
triggers.pop(0).set()
time.sleep(interval)
if __name__ == '__main__':
logger.info( "Starting" )
out_queue = mp.Queue()
#Initialise the load processes
nprocs, procs = 3, []
triggers = [mp.Event() for _ in range(nprocs)]
triggers[0].set() #set the first process to trigger immediately
for i, trigger in enumerate(triggers):
p = mp.Process( name='data_loader %d'%i, target=data_loader,
args=(trigger, out_queue) )
procs.append( p )
for p in procs:
p.start()
#Monitoring process
qm = mp.Process( name='queue_monitor', target=queue_monitor,
args=(out_queue, triggers) )
qm.start()
#consume data
while out_queue.empty():
pass
else:
for d in iter( out_queue.get, None ):
time.sleep(0.2) #pretend to take some time to process/animate the data
logger.info( 'data: %i' %d[0,0] ) #just to keep track of data ordering
在某些情况下,这种方法非常出色,但有时在触发新的加载过程后,数据的顺序会混乱。我无法弄清楚为什么会发生这种情况 - mp.Queue应该是FIFO吗?!例如。按原样运行上面的代码将不会在输出队列中保留正确的顺序,但是,将阈值更改为较低的值,例如。 30解决了这个问题。 *很困惑......
所以问题:如何在python中使用multiprocessing
正确实现这种交错加载策略?
答案 0 :(得分:3)
这看起来像一个缓冲问题。在内部,multiprocessing.Queue
使用缓冲区临时存储您已入队的项目,并最终将它们刷新到后台线程中的Pipe
。只有在发生冲洗之后,才会将项目实际发送到其他进程。因为您要将大型对象放到Queue
上,所以会有很多缓冲。这导致加载过程实际重叠,即使您的日志记录显示一个进程在另一个进程开始之前完成。文档实际上有关于这种情况的警告:
当一个对象被放入队列时,该对象被腌制并且a 后台线程稍后将pickle数据刷新到底层 管。这有一些后果,但有点令人惊讶 不应该造成任何实际困难 - 如果他们真的很烦 然后,您可以使用由经理创建的队列。
- 将对象放在空队列后,队列的
empty()
方法返回False之前可能会有无穷小的延迟 并且get_nowait()
可以在不提出Queue.Empty
的情况下返回。- 如果多个进程将对象排入队列,则可能无序地在另一端接收对象。然而, 由相同进程排队的对象将始终处于预期状态 彼此订购。
醇>
我建议您按照文档状态进行操作,然后使用multiprocessing.Manager
创建队列:
m = mp.Manager()
out_queue = m.Queue()
这将让您完全避免这个问题。
另一种选择是只使用一个进程来完成所有数据加载,并让它在循环中运行,并在循环顶部调用event.wait()
。