C++ tbb并行_流水线中的粒度调整

C++ tbb并行_流水线中的粒度调整,c++,multithreading,pipeline,tbb,C++,Multithreading,Pipeline,Tbb,管道的任务如下: 按顺序读取大量(10-15k)的~100-200 Mb压缩文件 并行解压缩每个文件 并行反序列化每个解压缩文件 处理结果反序列化对象,并基于所有对象(平均值、中值、分组等)获取一些值 当我得到解压缩的文件内存缓冲区时,序列化的块会一个接一个地移动,所以我希望以相同的方式将它们传递给下一个过滤器,或者,至少,通过将序列化的块打包成若干组,然后传递来调整这个过程。然而(据我所知),tbb_管道使我将指针传递给所有序列化块的缓冲区,因为每个过滤器必须获取指针并返回指针 据我所知,使用

管道的任务如下:

  • 按顺序读取大量(10-15k)的~100-200 Mb压缩文件
  • 并行解压缩每个文件
  • 并行反序列化每个解压缩文件
  • 处理结果反序列化对象,并基于所有对象(平均值、中值、分组等)获取一些值
  • 当我得到解压缩的文件内存缓冲区时,序列化的块会一个接一个地移动,所以我希望以相同的方式将它们传递给下一个过滤器,或者,至少,通过将序列化的块打包成若干组,然后传递来调整这个过程。然而(据我所知),tbb_管道使我将指针传递给所有序列化块的缓冲区,因为每个过滤器必须获取指针并返回指针

    据我所知,使用并发队列来积累序列化对象包会扼杀使用tbb_管道的问题。此外,过滤器中运算符()的常量不允许有我自己的中间“任务池”(不过,如果每个线程都有自己的本地“任务”存储副本,并从中剪切正确的片段,那就太好了)

    主要问题: 在这种情况下,有没有办法“调整”粒度?(即,某些筛选器获取指向所有序列化对象的指针,并传递给下一个筛选器小对象包)

    重新格式化(分割等)输入文件几乎是不可能的

    第二个问题: 当我积累处理结果时,我并不真正关心任何类型的顺序,我只需要汇总统计数据。我是否可以使用并行过滤器而不是顺序串行过滤器,将每个线程的处理结果累加到某个地方,然后合并它们

    然而(据我所知),tbb_管道使我将指针传递给所有序列化块的缓冲区,因为每个过滤器必须获取指针并返回指针

    首先,我认为最好使用更现代、类型更安全的管道形式:。它没有规定您传递任何特定数据的任何特定指针。您只需指定下一阶段处理它所需的数据类型。因此,问题在于第一个过滤器如何划分要由以下过滤器处理的数据

    主要问题:在这种情况下,是否有办法“调整”粒度?(即,某些筛选器获取指向所有序列化对象的指针,并传递给下一个筛选器小对象包)

    您可以安全地将一个并行算法嵌入到另一个并行算法中,以更改某些阶段的粒度,例如,在顶层,第一个管道通过文件列表;第二条管道在嵌套级别读取文件的大块;最后,最里面的管道将一些第二级阶段的大区块分解为小区块。请参见下面的嵌套的一般示例

    第二个问题:我是否可以使用并行过滤器而不是顺序串行过滤器,并将每个线程的处理结果累积到某个位置,然后将它们合并

    是的,如果并行筛选器不修改共享数据,则始终可以使用并行筛选器。例如,您可以使用以收集特定于线程的部分和,然后将它们合并

    不过,如果每个线程都有自己的本地存储副本用于“任务”,并从中剪切出正确的片段,那就太好了

    是的,他们有。每个线程都有自己的本地任务池


    嵌套并行_管道的一般示例

    parallel_pipeline( 2/*only two files at once*/,
        make_filter<void,std::string>(
            filter::serial,
            [&](flow_control& fc)-> std::string {
                if( !files.empty() ) {
                    std::string filename = files.front();
                    files.pop();
                    return filename;
                 } else {
                    fc.stop();
                    return "stop";
                }
            }    
        ) &
        make_filter<std::string,void>(
            filter::parallel,
            [](std::string s) {
    
                // a nested pipeline
                parallel_pipeline( 1024/*only two files at once*/,
                    make_filter<void,char>(
                        filter::serial,
                        [&s](flow_control& fc)-> char {
                            if( !s.empty() ) {
                                char c = s.back();
                                s.pop_back();
                                return c;
                             } else {
                                fc.stop();
                                return 0;
                            }
                        }    
                    ) &
                    make_filter<char,void>(
                        filter::parallel,
                        [](char c) {
                            putc(c, stdout);
                        } 
                    )
                );
            } 
        )
    );
    
    parallel_pipeline(2/*一次仅两个文件*/,
    制造过滤器(
    过滤器::串行,
    [&](流量控制&fc)->标准::字符串{
    如果(!files.empty()){
    std::string filename=files.front();
    pop()文件;
    返回文件名;
    }否则{
    fc.stop();
    返回“停止”;
    }
    }    
    ) &
    制造过滤器(
    过滤器::并行,
    [](标准::字符串s){
    //嵌套管道
    并行_管道(1024/*一次仅两个文件*/,
    制造过滤器(
    过滤器::串行,
    [&s](流量控制&fc)->字符{
    如果(!s.empty()){
    char c=s.back();
    s、 向后弹出();
    返回c;
    }否则{
    fc.stop();
    返回0;
    }
    }    
    ) &
    制造过滤器(
    过滤器::并行,
    [](字符c){
    putc(c,stdout);
    } 
    )
    );
    } 
    )
    );
    
    1。在我提到的标题tbb_parallel_pipeline中,有问题的文本如此短别名命中实际的Previous版本名=)无论如何,感谢您的关注2。combinable解决了这个任务,很抱歉在文档中没有注意到它。就我所见,合并进程只消耗一个线程。有没有办法与之平行?(在我的例子中,合并是关于合并映射的映射,所以遍历args和填充结果映射)3。嵌套管道非常好,tbb文档确实错过了此类细节4。“您可以安全地将一个并行算法嵌入到另一个并行算法中,以便更改某些阶段的粒度”-这是否也与并行*有关?(safety=对于管道任务平衡器或类似的东西来说是最优和可管理的)@iamiggor,是的,任何并行算法都可以嵌套到另一个中。。甚至递归地。所有高级算法都基于由公共TBB任务调度器管理的任务。它提供了可组合性。据我所知,组合进程只消耗一个线程。有没有办法与之平行?(在我的例子中,合并是关于合并映射的映射,所以遍历args和填充结果映射)看看
    parallel\u re