调整tbb parallel_pipeline中的粒度

时间:2014-08-05 14:54:50

标签: c++ multithreading pipeline tbb

管道任务如下:

  1. 按顺序读取大量(10-15k)量的~100-200 Mb压缩文件
  2. 并行解压缩每个文件
  3. 并行反序列化每个解压缩文件
  4. 处理结果反序列化对象并根据所有对象(平均值,中位数,灌浆等)获取一些值。
  5. 当我得到解压缩的文件内存缓冲区时,序列化的块一个接一个地进行,所以我想以相同的方式将它们传递给下一个过滤器,或者至少通过将序列化块打包成一组来调整这个过程数字然后通过。但是(据我所知)tbb_pipeline让我将指针传递给包含所有序列化块的缓冲区,因为每个过滤器都必须获取指针并返回指针。

    正如我所理解的,使用并发队列来累积序列化对象包可以解决使用tbb_pipeline的问题。此外,过滤器中 operator()的常量不允许拥有我自己的中间“任务池”(但是,如果每个线程都有自己的“任务”存储的本地副本,并且只是向右切割它的碎片,它会很棒)

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

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

    次要问题: 当我累积处理结果时,我并不关心任何类型的订单,我只需要汇总统计数据。我可以使用 parallel 过滤器而不是 serial_out_of_order 并在某处累积每个线程的处理结果,然后合并它们吗?

1 个答案:

答案 0 :(得分:2)

  

然而(据我所知)tbb_pipeline让我将指针传递给包含所有序列化块的缓冲区,因为每个过滤器都必须获取指针并返回指针。

首先我认为,最好使用更现代,类型安全的管道形式:parallel_pipeline。它没有规定您传递任何特定数据的任何特定指针。您只需指定下一阶段需要哪种类型的数据才能处理它。因此,第一个过滤器如何对要由以下过滤器处理的数据进行分区。

  

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

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

  

次要问题:我可以使用并行过滤器而不是serial_out_of_order并在某处累积每个线程的处理结果,然后合并它们吗?

是的,如果不修改共享数据,您始终可以使用并行过滤器。例如,您可以使用tbb::combinable来收集特定于线程的部分和,然后将它们组合起来。

  

但是,如果每个帖子都有自己的“任务”存储本地副本,只是从中删除了正确的部分,那就太棒了

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


嵌套parallel_pipelines的一般示例

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);
                    } 
                )
            );
        } 
    )
);