c++中并行的vector的find_first

find_first of a vector in parallel in C++

本文关键字:first find vector 并行 c++      更新时间:2023-10-16

我有一个相当大的向量。向量的一些成员平行地匹配某个条件。我想找到第一个符合条件的元素。

我的问题非常类似于这个问题(tbb: parallel find第一个元素),但我没有tbb。检查条件是非常繁琐的(所以我不能按顺序对所有条件进行检查)。这就是为什么我想并行运行它。我必须提到,我想找到第一个元素(所以元素的索引位置对我来说很重要)。

例如我有4个线程。

ThreadNr   Index      condition
1            0         Not Meet
2            1         Not Meet
3            2         Not Meet
4            3         Not Meet
ThreadNr   Index      condition
1            4         Not Meet
2            5          Meet
3            6         Not Meet
4            7          Meet

函数必须返回索引号为5。线程必须分布并在顺序迭代块上工作(块大小可以大于1)。例如,线程1处理前4个元素,线程2处理后4个元素,依此类推)。

在上面的例子中,如果4号线程(在索引7中)在2号线程(在索引5中)之前找到成员,它必须等待所有线程完成任务。正如我之前所说,最低的指数是目标。

如果你有更好的算法,请纠正我。

注意:我可以使用外部库,如boost 1.62, OpenMP 2.0

由于OpenMP 2.0没有取消结构,您必须自己实现一个,例如,通过使用共享变量。这也意味着您不能使用for工作共享结构,因为不允许打破并行循环(这就是OpenMP 4.0引入取消结构的原因)。如果在每个元素的求值之间实现取消检查,可能会出现两个或多个线程找到符合条件的元素。因此,您应该对索引执行最小还原:

int found = 0;
int first_index = INVALID_VALUE;
int iteration = 0;
#pragma omp parallel
{
   int my_index = INVALID_VALUE;
   int i;
   do
   {
      // Later versions of OpenMP allow for "atomic capture"
      // but OpenMP 2.0 requires a critical directive instead
      #pragma omp critical(iteration)
      {
         i = iteration++;
      }
      if (i < N && check(i))
      {
         found = 1;
         my_index = i;
      }
   } while (!found && i < N);
   #pragma omp critical(reduction)
   if (my_index != INVALID_VALUE)
   {
      if (first_index == INVALID_VALUE || my_index < first_index)
         first_index = my_index;
   }
   // Only needed if more code follows before the end of the region
   #pragma omp barrier
   ...
}

这段代码假设检查第i个元素(check(i))的条件不会改变元素的状态,因此,可能发生的最坏情况是,找到匹配元素的线程可能不得不等待所有其他线程完成对当前正在处理的元素的检查,等待时间将是所有处理时间中的最大值。

do循环中使用的critical结构是昂贵的。如果check()不需要那么多时间,那么您可以考虑使用块而不是迭代:

do
{
   #pragma omp critical(chunk)
   {
       my_chunk = chunk++;
   }
   if (my_chunk >= N_chunks)
      break;
   for (i = my_chunk * chunk_size; !found && i < (my_chunk+1)*chunk_size; i++)
   {
      if (check(i))
      {
         found = 1;
         my_index = i;
         break;
      }
   }
} while (!found && my_chunk < N_chunks);

另一种解决方案,当元素数量不是很大且检查每个元素的成本很高时,效果相当好:

#pragma omp parallel
{
   #pragma omp for schedule(dynamic,x)
   for (i = 0; i < N; i++)
   {
      if (!found && check(i))
      {
         my_index = i;
         found = 1;
      }
   }
   // Min reduction from the solution above
   ...
}

一旦found为真,其余的循环迭代将运行"空"体,因为&&的快捷属性

使用OpenMP,您可以尝试使用#pragma omp for schedule(dynamic)构建for循环。每个线程将以与vector相同的顺序执行一次迭代。如果你想按线程检查4个元素,试试#pragma omp for schedule(dynamic, 4)