gpt4 book ai didi

c++ - OpenMP/C++ : Parallel for loop with reduction afterwards - best practice?

转载 作者:行者123 更新时间:2023-11-30 05:41:56 25 4
gpt4 key购买 nike

给定以下代码...

for (size_t i = 0; i < clusters.size(); ++i)
{
const std::set<int>& cluster = clusters[i];
// ... expensive calculations ...
for (int j : cluster)
velocity[j] += f(j);
}

...我想在多个 CPU/内核上运行。函数f不使用 velocity .

一个简单的#pragma omp parallel for在第一个 for 循环之前会产生不可预测的/错误的结果,因为 std::vector<T> velocity在内循环中修改。多个线程可以访问和(尝试)修改 velocity 的相同元素同时。

我认为第一个解决方案是写 #pragma omp atomicvelocity[j] += f(j); 之前手术。这给了我一个编译错误(可能与类型为 Eigen::Vector3dvelocity 的元素是类成员有关)。此外,我读到原子操作非常与为每个线程设置一个私有(private)变量并在最后进行归约相比。这就是我想做的,我想。

我想出了这个:

#pragma omp parallel
{
// these variables are local to each thread
std::vector<Eigen::Vector3d> velocity_local(velocity.size());
std::fill(velocity_local.begin(), velocity_local.end(), Eigen::Vector3d(0,0,0));

#pragma omp for
for (size_t i = 0; i < clusters.size(); ++i)
{
const std::set<int>& cluster = clusters[i];
// ... expensive calculations ...
for (int j : cluster)
velocity_local[j] += f(j); // save results from the previous calculations
}

// now each thread can save its results to the global variable
#pragma omp critical
{
for (size_t i = 0; i < velocity_local.size(); ++i)
velocity[i] += velocity_local[i];
}
}

这是一个好的解决方案吗?它是最佳解决方案吗? (它甚至正确吗?)

进一步思考:使用 reduce子句(而不是 critical 部分)会引发编译器错误。我认为这是因为 velocity是类(class)成员。

我试图找到一个有类似问题的问题,并且this问题看起来几乎一样。但我认为我的情况可能不同,因为最后一步包括 for环形。这是否是最佳方法的问题仍然存在。

编辑: 根据评论要求:reduction子句...

    #pragma omp parallel reduction(+:velocity)
for (omp_int i = 0; i < velocity_local.size(); ++i)
velocity[i] += velocity_local[i];

...抛出以下错误:

错误 C3028:“ShapeMatching::velocity”:在数据共享子句中只能使用变量或静态数据成员

(与 g++ 类似的错误)

最佳答案

您正在进行数组缩减。我已经多次描述过这个(例如 reducing an array in openmpfill histograms array reduction in parallel with openmp without using a critical section )。您可以在有和没有关键部分的情况下执行此操作。

您已经在关键部分(在您最近的编辑中)正确地做到了这一点,所以让我描述一下如何在没有关键部分的情况下做到这一点。


std::vector<Eigen::Vector3d> velocitya;
#pragma omp parallel
{
const int nthreads = omp_get_num_threads();
const int ithread = omp_get_thread_num();
const int vsize = velocity.size();

#pragma omp single
velocitya.resize(vsize*nthreads);
std::fill(velocitya.begin()+vsize*ithread, velocitya.begin()+vsize*(ithread+1),
Eigen::Vector3d(0,0,0));

#pragma omp for schedule(static)
for (size_t i = 0; i < clusters.size(); i++) {
const std::set<int>& cluster = clusters[i];
// ... expensive calculations ...
for (int j : cluster) velocitya[ithread*vsize+j] += f(j);
}

#pragma omp for schedule(static)
for(int i=0; i<vsize; i++) {
for(int t=0; t<nthreads; t++) {
velocity[i] += velocitya[vsize*t + i];
}
}
}

由于我没有做过的错误共享,此方法需要额外的注意/调整。

至于哪种方法更好,您将不得不进行测试。

关于c++ - OpenMP/C++ : Parallel for loop with reduction afterwards - best practice?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30943452/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com