为什么这种搜索方法不可扩展?

时间:2015-04-24 16:57:46

标签: c++ parallel-processing openmp

我想使用openMP并行搜索算法,vTree是一个二叉搜索树,我想为每个点集应用我的搜索算法。下面是我的代码片段。两点的搜索过程完全不相关,因此可以是并行的。虽然他们确实需要阅读同一棵树,但一旦构建,树就不会被修改了。因此它是只读的。

但是,下面的代码显示了可怕的可扩展性,在我的32核平台上,只实现了2倍的加速。是因为所有线程都读取vTree?如果是这样,我该如何进一步优化代码?

    auto results = vector<vector<Point>>(particleNum);
    auto t3 = high_resolution_clock::now();
    double radius = 1.6;
#pragma omp parallel for
    for (decltype(points.size()) i = 0; i < points.size(); i++)
    {
        vTree.search(points[i], radius, results[i]);
    }
    auto t4 = high_resolution_clock::now();
    double searchTime = duration_cast<duration<double>>(t4 - t3).count();

search的类型签名是

void VPTree::search(const Point& p, double radius, vector<Point>& result) const

搜索结果将被放入result

1 个答案:

答案 0 :(得分:3)

我最好的猜测是你在结果向量上缓存ping-pong。我会假设你的搜索&#34;函数使用传入的结果向量作为放置点的位置,并在整个算法中使用它来在搜索树中遇到它们时插入邻居。每当向该结果向量添加一个点时,该向量对象的内部数据都将被修改。并且因为所有结果向量在连续内存中打包在一起,所以不同的结果向量可能占用相同的高速缓存行。因此,当CPU保持缓存一致性时,它将不断锁定相关的缓存行。

解决它的方法是使用一个内部的临时向量,你只能在结尾处分配给结果向量(如果使用移动语义,可以很便宜地完成)。像这样:

void VPTree::search(const Point& p, double radius, vector<Point>& result) const {
  vector<Point> tmp_result;
  // ... add results to "tmp_result"
  result = std::move(tmp_result);
  return;
}

或者,您也可以按值返回向量(隐式使用移动):

vector<Point> VPTree::search(const Point& p, double radius) const {
  vector<Point> result;
  // ... add results to "result"
  return result;
}

欢迎来到移动语义的快乐世界,以及它在解决这些类型的并发/缓存一致性问题方面有多棒。

您也可以想到,您遇到了与从所有线程访问同一个树相关的问题,但由于它是所有只读操作,我很确定即使是保守的像x86(和其他Intel / AMD CPU)这样的架构不应该构成重大问题,但我可能错了(也许是一种&#34;超额认购&#34;问题可能会起作用,但它&#39 ;可疑的)。而其他问题可能包括OpenMP确实会产生相当多的开销(产生线程,同步等)这一事实,而这些开销必须根据您在这些并行循环中执行的实际操作的计算成本进行加权(并且&#39 ;并不总是一个有利的权衡)。而且,如果您的VPTree(我想象的是&#34; Vantage-point Tree&#34;)没有良好的引用位置(例如,您将其实现为链接树),那么性能将会是你使用它的方式很糟糕(正如我解释here)。