OpenMP 用于带有 std::vector 和标量变量的循环,带有缩减

OpenMP for loop with std::vector and scalar variable with reduction

本文关键字:循环 变量 用于 std vector OpenMP 标量      更新时间:2023-10-16

我有这个代码:

#pragma omp declare reduction(* : scalar : omp_out *= omp_in)     
scalar like=1;
vector<scalar>& bigsum;
#pragma omp parallel for // reduction(* : like)    
for (int m = 0; m < M-1; m++)
   like *= bigsum[m];

我正在尝试获得一致的结果,但它没有(竞争条件问题),但我应该如何解决它? 因为它在代码中可见,我有自己的reduction function但它也不起作用。对于标量和 std::vector 有什么技巧是我应该注意的吗?

这里的标量变量只是通过对我创建的每个双精度值的应用 log() 覆盖浮点数,因为有这么多的双倍到双倍乘法,并且几个双倍之后的结果变得接近于零。例如,通过执行log(),乘法变为加法和等。

一致答案的一个答案是:

    #pragma omp parallel
    {
       scalar loc = 1;
    #pragma omp for
       for (std::size_t m = 1; m < M;m++)
       {
          _flm[m-1] = Qratio[m-1] * k1 + k2;
          bigsum[m-1] = kappa0omegaproduct + kappa[1] * _flm[m-1];
    #pragma omp critical (reduce_product)
          {
             like *= bigsum[m-1];
          }
       }
}

这个答案是正确的,但太慢了,在我的 8 核机器上几乎慢了 8 倍!

三天

后我自己有一个答案,并对我的发现有一个解释。

我创建了自己的归约函数,如下所示:

#pragma omp declare reduction(* : scalar : omp_out *= omp_in) initializer (omp_priv (1))

诀窍是omp_priv,显然减少值初始化很重要,我在这里学到了一些东西。

我通过对这样的循环应用 openmp 使代码简单得多:

#pragma omp parallel for reduction (* : like)

非常简单干净。以这种新方式,代码得到并行化,并且运行速度比我在问题正文中的速度更快。不幸的是,它仍然比串行版本慢。也许是因为 std::vector 的使用,或者重载的算术运算非常慢!?我不知道。代码是如此之大,我无法以可以理解的方式将其复制粘贴到此处,并且对于其他人来说阅读也不会很痛苦。

您的初始示例依赖于本机 OpenMP 缩减。您确定用户定义的 OpenMP 缩减工作正常吗?这可能是重载运算符的问题。

你的第二个例子是正确的(你说的),但由于关键部分,速度很慢。为什么不手动实现 OpenMP 缩减,方法是在每个线程上有一个局部变量(例如"local_like"),然后在"omp for"之后使用关键部分?