Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/133.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 容器元素上的OpenMP缩减_C++_Openmp_Reduction - Fatal编程技术网

C++ 容器元素上的OpenMP缩减

C++ 容器元素上的OpenMP缩减,c++,openmp,reduction,C++,Openmp,Reduction,我有一个嵌套循环,外部迭代很少,内部迭代很多。在内部循环中,我需要计算一个和,所以我想使用OpenMP缩减。外部循环位于容器上,因此减少应该发生在该容器的元素上。 下面是一个最简单的人为示例: #include <omp.h> #include <vector> #include <iostream> int main(){ constexpr int n { 128 }; std::vector<int> vec (4, 0)

我有一个嵌套循环,外部迭代很少,内部迭代很多。在内部循环中,我需要计算一个和,所以我想使用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”没有指针或数组类型,这足以说明如何找到解决方法。然而,这意味着我必须引入一个新的变量,并在一定程度上更改代码逻辑,我发现不太清楚代码应该做什么

我的主要问题是,是否有更好/更干净/更标准的方法来编写容器元素的缩减

我还想知道上面代码中显示的第三种方式为什么以及如何工作。我实际上是在使用Eigen库,在它的容器上,这个变体似乎工作得很好,但是还没有对它进行广泛的测试,但是在std::vector上,它会产生介于0和实际结果8128之间的结果。我认为它应该可以工作,因为vec[I]和val[I]都应该计算为对同一地址的解引用。但遗憾的是,显然不是


我使用的是OpenMP 4.5和gcc 9.3.0。

对于第二个示例,与其存储指针然后总是访问同一个元素,不如使用局部变量:

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

对于第三个循环,我怀疑问题是因为reduce子句命名了一个变量,但在循环中您从未使用该名称更新该变量,因此编译器看不到任何要减少的内容。使用Eigen可能会使代码的分析更加复杂,从而导致循环工作。

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

一,。在上面的示例中,使用std::vec执行OpenMP缩减的最佳方法是什么

我使用您的方法,即创建一个指针int*val{&vec[0]}

ii声明一个新的共享变量,如@1201programalam

iii声明一个不适用于您的简单案例,但请参见3。下面是一个更有效的模式

二,。为什么第三个循环不起作用,为什么它和本征一起起作用

与前面的回答一样,您告诉OpenMP对内存地址X执行缩减和,但对内存地址Y执行加法,这意味着将忽略缩减声明,并且加法将受到通常的线程竞争条件的约束

您并没有提供关于您的Eigen venture的太多细节,但以下是一些可能的解释:

我认为您并没有真正使用多线程检查n=Eigen::nbThreads

ii您没有禁用Eigen自身的并行性,这可能会中断您自己对OpenMP的使用,例如Eigen\u DONT\u PARALLELIZE编译器指令

iii竞争条件存在,但您没有看到,因为特征操作需要更长的时间,您使用的线程数量较少,只写入了少量的值=>线程相互干扰的发生率较低,从而产生错误的结果

三,。我应该如何使用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()))

在使用它的函数之前声明缩减,您就可以开始了

谢谢您的回复,但是仍然有相同数量的新变量,现在还有更多的操作,所以这不是我想要的。谢谢您的回答!我最初希望对内环和外环的线程数有更多的控制,但我想现在我会使用collapse2。我认为,用户定义的缩减绝对是首选的解决方案。它绝对是最具可读性的,特别是因为reduce+:可以重载。还有一个问题:我非常喜欢为模板类型定义缩减,尤其是与Eigen一起使用时。可能吗?到目前为止,我的编译器说这里不允许使用“pragma”。对于第2部分:ii,定义了令牌,我检查了I的omp_get_num_线程。不过,对于我的测试用例,很可能是iii,因为迭代次数很少。问题发生在较大的阵列上。afaik不可能将模板直接用于OpenMP缩减。您可能必须在归约声明中使用泛型函子,而不是原语,这样才能工作。然而,我还没有试过。