TBB 线程本地集使用组合或 enumerable_thread_specific?

TBB Thread Local Set Using combinable or enumerable_thread_specific?

我想 运行 tbb::parallel_for 处理大型数据集并生成一个唯一的数据集。 parallel_for 主体中包含一些附加逻辑,用于确定原始数据集的每个子元素是否应包含在此集中。结果集通常比原始数据集小得多,我宁愿不计算具有重复项的向量并在之后删除重复项,因为这会增加内存使用量。

我的第一个实现使用 tbb::concurrent_unordered_set,通过分析,我注意到 set.insert() 方法中存在严重的性能瓶颈。

我尝试改进这一点是尝试使用线程本地存储为每个线程生成一个集合,然后在最后组合这些集合以删除原子。

尽管阅读了很多文档,但我仍然不确定 tbb::combinable 还是 tbb::enumerable_thread_specific 最适合。

这一定是一个相当常见的用例。有人可以提供示例实现或指出我可以查看的在线示例吗?

我认为你的方向是正确的。并发哈希表对于大量元素(数千)是有效的。尽管您仍然可以尝试在 运行 您的算法之前保留足够的容量并使用 concurrent_unordered_set 的负载因子(设置为 1)并尝试 concurrent_hash_map (使用时速度更快insert(value)没有访问器,也需要预留一些容量)。

tbb::combinabletbb::enumerable_thread_specific 都使用相同的后端实现。区别仅在于界面。 documentation 有后者的例子,我重新设计了一下:

typedef tbb::enumerable_thread_specific< std::pair<int,int> > CounterType;
CounterType MyCounters (std::make_pair(0,0));

int main() {
    tbb::parallel_for( tbb::blocked_range<int>(0, 100000000),
      [](const tbb::blocked_range<int> &r) {
        CounterType::reference my_counter = MyCounters.local();
        ++my_counter.first; my_counter.second += r.size();
    });

    std::pair<int,int> sum = MyCounters.combine(
        [](std::pair<int,int> x, std::pair<int,int> y) {
            return std::make_pair(x.first+y.first, x.second+y.second);
        });
    printf("Total calls to operator() = %d, "
         "total iterations = %d\n", sum.first, sum.second);
}

最后,也尝试替代方法,在不需要其他方法(如可组合)的情况下使用 tbb::parallel_reduce,此外,减少主要是并行完成的(仅记录 P 顺序步骤,同时组合线程特定值需要顺序访问所有 P 元素)。