我正在编写一个使用`tbb :: parallel_pipeline'处理视频流的应用程序。我的第一个过滤器包含两个重要的操作,一个必须紧接另一个执行。
我的测试表明,当我将max_number_of_live_tokens
设置为6(我拥有的滤波器数量)时,两次操作之间的延迟在3到20毫秒之间,但是在max_number_of_live_tokens
为1的情况下始终在3到4毫秒之间。第一种情况对于我的应用程序是不可接受的,但是我需要允许多个令牌同时运行以利用并行性。
这里是我的管道设置:
tbb::parallel_pipeline(6, //max_number_of_live_tokens
// 1st Filter
tbb::make_filter< void, shared_ptr<PipelinePacket_t> >(tbb::filter::serial_in_order,
[&](tbb::flow_control& fc)->shared_ptr<PipelinePacket_t>
{
shared_ptr<PipelinePacket_t> pPacket = grabFrame();
return pPacket;
}
)
&
... // 5 other filters that process the image - all 'serial_in_order'
);
这是我的grabFrame()
函数:
shared_ptr<VisionPipeline::PipelinePacket_t> VisionPipeline::grabFrame() {
shared_ptr<PipelinePacket_t> pPacket(new PipelinePacket_t);
m_cap >> pPacket->frame; // Operation A (use opencv api to capture frame)
pPacket->motion.gyroDeg = m_imu.getGyroZDeg(); // Operation B (read a gyro value)
return pPacket;
}
我需要操作A和B尽可能彼此靠近(以便陀螺仪值反映捕获帧时的值)。
[我的猜测是,多个令牌同时飞行时发生的抖动是由与第一个过滤器在同一线程上运行并在执行grabFrame()
时中断它的其他过滤器的任务引起的。我已经浏览了一些TBB文档,但找不到关于parallel_pipeline
如何将过滤器分解为任务的任何信息,因此我不清楚TBB是否以某种方式将grabFrame()
分解为多个TBB任务。
此假设正确吗?如果是这样,我如何告诉tbb不要通过其他任务中断操作A和操作B之间的第一个过滤器?
OpenCV本身在内部使用TBB进行各种操作。因此,如果这实际上与TBB相关,则不是因为您在A和B之间被打扰,而是OpenCV本身正在与其余过滤器链争夺优先权。不过不太可能。
因此,我不清楚TBB是否以某种方式将grabFrame()分解为多个TBB任务。
这永远不会发生。除非其中有部分通过TBB进行明确调度,否则它没有任何作用。 TBB并不是神奇地将您的功能拆分为任务。
但是那可能甚至不是你唯一的问题。如果您的过滤器恰好占用大量内存带宽,则可能是仅通过并行执行图像处理而大大降低了实际捕获过程的速度。
好像您正在通过5个过滤器连续运行完整图像,对吗?全分辨率,不是平铺?如果是这样,那么大多数这些过滤器可能不受ALU的限制,而是受内存带宽的限制,因为您也不是处于CPU缓存范围之内。
如果希望并行处理,则必须在过滤器级之间消除对主存储器的写回。唯一的方法是要么在过滤器链的输入过滤器中平铺图像,要么编写自定义的多合一过滤器内核。如果在该链中具有空间相关性的过滤器,显然这不像我说的那么简单,那么您就必须在上级中包含一些重叠。
max_number_of_live_tokens
然后实际上具有真实含义。这是飞行中的“平铺”数量。这样做的主要目的不是将每个筛选器阶段限制为仅一个并发执行(无论如何都不会发生),而是要控制最大工作集大小。
例如如果您知道每个磁贴现在的大小均为128kB,则每个过滤器(源和目标)都涉及2个副本,并且您知道有2MB的L3缓存,那么您就可以负担得起有8个令牌在飞行中,而不会溢出到主内存中。如果您还恰好具有(至少)8个CPU内核,那么这将产生理想的吞吐量,但是即使您没有,也至少不会冒超过高速缓存大小而成为瓶颈的风险。