用于并行化std :: set missing operator-()的cilk_for错误

时间:2014-12-06 19:13:29

标签: c++ c++11 parallel-processing cilk-plus

我试图使用cilk_for来迭代集合。事实证明它没有为set定义operator-(..)。 Cilk_for教程解释了原因,但未提供任何处理此类案例的示例。他们说应该提供以下内容:但我不确定在何处以及如何设置值:

链接是here

difference_type operator-(termination_type, variable_type); // where to use this?

//my example
auto func = std::bind (my_functor, std::placeholders::_1);  

std::set<vertex_id> vertex_set;
// fill the vertex_set with some vertex ids

cilk_for (auto it = vertex_set.begin(); it!=vertex_set.end(); ++it) {
   func(*it) 
}

我如何以及在哪里为cilk编译器提供操作符(..)来处理它?<​​/ p>

variable_typeset::iterator。差异类型为difference_type (ptrdiff_t),但根据其示例,termination_type是什么?

1 个答案:

答案 0 :(得分:1)

问题不在于终止表达式,它!= vertex_set.end(),这很好。在这种情况下,termination_type只是std :: set :: iterator。

问题是std :: set没有随机访问迭代器,因此没有operator-或operator + =。换句话说,您不能在恒定时间内推进迭代器,并且无法在恒定时间内计算两个迭代器之间的距离。没有合理的方法添加这些缺少的运算符。 cilk_for循环根本不用于遍历线性或树状结构的容器,如list或set。

要理解为什么会这样,请考虑并行执行循环所需的内容。首先,您必须将数据设置为大致相等的块,然后您必须将这些块分配给并行工作者。循环的每次迭代必须能够立即转到预期要处理的输入块。如果它需要线性遍历整个容器以便找到它的块的开头,那么你已经在很大程度上打败了使用并行循环的目的。

有几种方法可以改善这种情况。第一个是考虑你的集合是否足够大,你正在做的操作集合是否足够昂贵,根本不需要并行性?如果没有,请使用串行循环并忘记它。如果是这样,请考虑使用不同的数据结构。可以用基于矢量的数据结构替换集合吗?在网上搜索,你会发现像Loki中的AssocVector这样的东西,它与std :: set具有相同的接口,但在其实现中使用vector并具有随机访问迭代器;它应该很容易修改或包装给std :: set接口而不是std :: map。它通常会超出std :: set或std :: map。

如果my_functor做了大量工作,您可以使用包含spawn的简单串行循环来分摊线性遍历的成本:

for (auto it = vertex_set.begin(); it!=vertex_set.end(); ++it) {
    cilk_spawn func(*it);
}
cilk_sync;

但要注意,如果func()相对较小,那么重复生成的开销将对性能产生明显的负面影响。

或者,您可以使用cilk_for,迭代索引而不是使用迭代器,如下面的代码所示:

cilk_for (std::size_t i = 0; i < vertex_set.size(); ++i) {
    auto it = vertex_set.begin();
    std::advance(it, i);  // Slow step!
    func(*it);
}

如果你的集合足够大,你可以通过将集合预先分块为迭代器向量来减少对std :: advance的调用次数。然后你可以并行迭代这些块:

// Each element of this vector is the start of a chunk of the set.
std::vector<std::set<vertex_id>::iterator> chunks;
// Create 1000 chunks
const auto chunk_size = vertex_set.size() / 1000;
auto chunk = vector_set.begin();
for (int i = 0; i < 1000; ++i) {
    chunks.push(chunk);
    advance(chunk, chunk_size);
}
chunks.push(vector_set.end());

// Now, process the chunks in parallel
cilk_for (int i = 0; i < 1000; ++i) {
    // Iterate over elements in the chunk.
    for (auto it = chunks[i]; it != chunks[i + 1]; ++it)
        func(*it);
}

根据您的具体情况,上述两个主题有很多变化。确保做一些分析和时间安排,以帮助您选择最佳方法。

我稍后会提到cilk_for的变体被认为可以对可以递归细分的任何数据结构进行操作(如树可以)。这样的变化可以直接解决你的问题,但我不能承诺什么时候可以提供这样的东西。