从基于线程的流水线转向基于任务的并行? (C++)

Posted

技术标签:

【中文标题】从基于线程的流水线转向基于任务的并行? (C++)【英文标题】:Moving from thread-based pipelining to task-based parallelism? (C++) 【发布时间】:2015-01-08 14:36:12 【问题描述】:

我正在研究如何将一些现有的 C++ 代码从基于线程的并行迁移到基于任务的并行,以及这种迁移是否可取。这是我的场景:

假设我有一些函数要在一个事件上执行。假设我有一台相机,每次一帧到达时,我都想做一些繁重的处理并保存结果。一些处理是串行的,所以如果我只是在同一个线程中串行处理每个帧,我不会得到充分的 CPU 利用率。假设帧每 33 毫秒到达一次,并且帧的处理延迟接近 100 毫秒。

因此,在我当前的实现中,我创建了 3 个线程来处理帧,并以循环方式将每个新帧分配给这些工作线程之一。所以线程 T0 可能会处理帧 F0、F3、F6 等。现在我得到了充分的 CPU 利用率,我不必为了保持实时速率而丢帧。

由于处理需要各种大的临时资源,我可以为每个工作线程预先分配这些资源。因此不必为每一帧重新分配它们。这种按线程分配资源的策略适用于粒度:如果它们是按帧分配的,这将花费很长时间,但是如果有更多的工作线程,我们就会耗尽资源。

我看不到使用标准 C++11 或 Microsoft 的 PPL 库将这种基于线程的并行性替换为基于任务的并行性的方法。如果有这样的模式可以在下面勾勒出来,我会很高兴学习它。

问题是在哪里存储状态 - 分配的临时资源(例如 GPU 内存) - 可以重新用于后续帧,但不得与当前处理帧的资源冲突。

在这种情况下是否还需要迁移到基于任务的并行性?

【问题讨论】:

你能把状态保存在static thread_local存储中吗? thread_local 变量不适用于任务。首先,标准规定所有 thread_local 变量必须在任务完成时销毁。其次,即使它们持续存在,您也无法保证可以在哪个线程上执行任务。 【参考方案1】:

我想通了。这是一个示例解决方案:

#include <iostream>
#include <ppltasks.h>
#include <thread>
#include <vector>

using PipelineState = int;
using PipelineStateArg = std::shared_ptr<PipelineState>;
using FrameState = int;
struct Pipeline

    PipelineStateArg state;
    concurrency::task<void> task;
;
std::vector<Pipeline> pipelines;

void proc(const FrameState& fs, PipelineState& ps)
 
    std::cout << "Process frame " << fs << " in pipeline " << ps << std::endl; 


void on_frame(int index)

    FrameState frame = index;
    if (index < 2)
    
        // Start a new pipeline
        auto state = std::make_shared<PipelineState>(index);
        pipelines.push_back(state, concurrency::create_task([=]() 
         
            proc(frame, *state); 
        ));
    
    else
    
        // Use an existing pipeline
        auto& pipeline = pipelines[index & 1];
        auto state = pipeline.state;
        pipeline.task = pipeline.task.then([=]() 
         
            proc(frame, *state); 
        );
    


void main()

    for (int i = 0; i < 100; ++i)
    
        on_frame(i);
        std::this_thread::sleep_for(std::chrono::milliseconds(33));
    
    for (auto& pipeline : pipelines)
        pipeline.tail.wait();

【讨论】:

对代码稍加解释会使其更有用,尤其是对于基于任务的并行性这样复杂的主题。

以上是关于从基于线程的流水线转向基于任务的并行? (C++)的主要内容,如果未能解决你的问题,请参考以下文章

虚幻4与现代C++:基于任务的并行编程与TaskGraph入门

线程可以做啥,而基于任务的异步模式(TAP)和任务并行(TPL)与任务(或任务<T>)不能做啥?

Manning新书C++并行实战,592页pdf,C++ Concurrency in Action

Python并行编程:基于线程的并行

查询处理器未能为执行并行查询启动必要的线程资源啥意思

多线程编程学习笔记——任务并行库