容器元素的OpenMP缩减

问题描述 投票:0回答:2

我有一个嵌套循环,几乎没有外部迭代,而有许多内部迭代。在内部循环中,我需要计算总和,因此我想使用OpenMP简化。外循环在容器上,因此减少应该发生在该容器的某个元素上。这是一个最小的人为例子:

#include <omp.h>
#include <vector>
#include <iostream>

int main(){
    constexpr int n { 128 };

    std::vector<int> vec (4, 0);
    for (unsigned int i {0}; i<vec.size(); ++i){

        /* this does not work */
        //#pragma omp parallel for reduction (+:vec[i])
        //for (int j=0; j<n; ++j)
        //  vec[i] +=j;

        /* this works */
        int* val { &vec[0] };
        #pragma omp parallel for reduction (+:val[i])
        for (int j=0; j<n; ++j)
            val[i] +=j;

        /* this is allowed, but looks very wrong. Produces wrong results
         * for std::vector, but on an Eigen type, it worked. */
        #pragma omp parallel for reduction (+:val[i])
        for (int j=0; j<n; ++j)
            vec[i] +=j;
    }
    for (unsigned int i=0; i<vec.size(); ++i) std::cout << vec[i] << " ";
    std::cout << "\n";

    return 0;
}

问题是,如果将reduce子句写为(+:vec[i]),则会得到错误‘vec’ does not have pointer or array type,该错误的描述性足以找到一种解决方法。但是,这意味着我必须引入一个新变量并稍稍更改代码逻辑,而发现代码应该执行的工作却不太明显。

我的主要问题是,是否有更好/更清洁/更标准的方式来减少容器元素的数量。

我也想知道上面代码[[somewhat中显示的第三种方式为何以及如何工作。我实际上正在使用Eigen库,该库的变体似乎可以正常工作(虽然尚未对其进行广泛测试),但是在std::vector上,它产生的结果介于零和实际结果之间(8128) )。我认为应该可以,因为vec[i]val[i] 应该都评估为取消引用相同的地址。但是a,显然不是。

我正在使用OpenMP 4.5和gcc 9.3.0。
c++ openmp reduction
2个回答
0
投票
对于第二个示例,不使用存储指针然后总是访问相同的元素,只需使用局部变量:

int val = vec[i]; #pragma omp parallel for reduction (+:val) for (int j=0; j<n; ++j) val +=j; vec[i] = val;

[使用第三循环,我怀疑问题是因为reduction子句命名了一个变量,但是您从未在循环中使用该名称来更新该变量,所以编译器认为没有什么减少。使用Eigen可能会使代码的分析更加复杂,从而导致循环工作。

0
投票
我将分三部分回答您的问题:

1。在上面的示例中,使用std::vec来执行OpenMP缩减的最佳方法是什么?i)使用您的方法,即创建一个指针int* val { &vec[0] };

ii)声明一个新的共享变量,如@ 1201ProgramAlarm已回答。

iii)声明一个user defined reduction(在您的简单情况下实际上并不适用,但是请参阅下面的3.了解更有效的模式)。

2。第三个循环为什么不起作用,为什么它与Eigen一起起作用?

像前面的回答状态,您告诉OpenMP对内存地址X执行缩减总和,但是您对内存地址Y执行加法,这意味着缩减声明将被忽略,并且您的加法受到通常的线程竞争的影响条件。

您并未真正提供有关Eigen合资企业的详细信息,但以下是一些可能的解释:

i)您并没有真正使用多个线程(检查n = Eigen::nbThreads( )

ii)您没有禁用Eigen自己的并行性,而并行性会破坏您自己对OpenMP的使用,例如EIGEN_DONT_PARALLELIZE编译器指令。

iii)竞争条件在那里,但是您没有看到它,因为Eigen操作花费的时间更长,您使用的线程数较少,并且仅写入了少量的值=>相互干扰的线程的发生率较低产生错误的结果。

3。我应该如何使用OpenMP并行处理这种情况(技术上不是您明确询问的问题)?

而不是只并行化内部循环,您应该同时并行化两者。序列号越少越好。在这种情况下,每个线程都有其自己的vec向量的私有副本,在所有元素由各自的线程求和之后,该副本将减少。该解决方案最适合您所演示的示例,但是如果您使用的向量很大,线程很多(或RAM十分有限),则可能会遇到RAM问题。

#pragma omp parallel for collapse(2) reduction(vsum : vec) for (unsigned int i {0}; i<vec.size(); ++i){ for (int j = 0; j < n; ++j) { vec[i] += j; } }

其中vsum是用户定义的减少量,即

#pragma omp declare reduction(vsum : std::vector<int> : std::transform(omp_out.begin(), omp_out.end(), omp_in.begin(), omp_out.begin(), std::plus<int>())) initializer(omp_priv = decltype(omp_orig)(omp_orig.size()))

在使用它的函数之前声明归约,你会很高兴的去做
© www.soinside.com 2019 - 2024. All rights reserved.