英特尔编译器 (C++) 问题与 std::vector 上的 OpenMP 减少有关

Posted

技术标签:

【中文标题】英特尔编译器 (C++) 问题与 std::vector 上的 OpenMP 减少有关【英文标题】:Intel compiler (C++) issue with OpenMP reduction on std::vector 【发布时间】:2018-10-17 18:21:24 【问题描述】:

从 OpenMP 4.0 开始,支持用户定义的缩减。所以我完全从here 定义了 C++ 中 std::vector 的缩减。它适用于 GNU/5.4.0 和 GNU/6.4.0,但它返回随机值以用于减少 intel/2018.1.163。

这是一个例子:

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

#pragma omp declare reduction(vec_double_plus : std::vector<double> : \
                              std::transform(omp_out.begin(), omp_out.end(), omp_in.begin(), omp_out.begin(), std::plus<double>())) \
                    initializer(omp_priv = omp_orig)

int main() 

    omp_set_num_threads(4);
    int size = 100;
    std::vector<double> w(size,0);

#pragma omp parallel for reduction(vec_double_plus:w)
    for (int i = 0; i < 4; ++i)
        for (int j = 0; j < w.size(); ++j)
            w[j] += 1;

    for(auto i:w)
        if(i != 4)
            std::cout << i << std::endl;

    return 0;

每个线程将所有 w 条目(其本地 w)加 1,最后将所有条目加在一起(减少)。对于 GNU,所有 w 条目的结果是 4,但对于 intel 编译器是随机的。有谁知道这里发生了什么?

【问题讨论】:

【参考方案1】:

这似乎是英特尔编译器中的一个错误,我可以使用不涉及向量的 C 示例可靠地重现它:

#include <stdio.h>

void my_sum_fun(int* outp, int* inp) 
    printf("%d @ %p += %d @ %p\n", *outp, outp, *inp, inp);
    *outp = *outp + *inp;


int my_init(int* orig) 
    printf("orig: %d @ %p\n", *orig, orig);
    return *orig;


#pragma omp declare reduction(my_sum : int : my_sum_fun(&omp_out, &omp_in) initializer(omp_priv = my_init(&omp_orig))

int main()
   
    int s = 0;
    #pragma omp parallel for reduction(my_sum : s)
    for (int i = 0; i < 2; i++)
        s+= 1;

    printf("sum: %d\n", s);

输出:

orig: 0 @ 0x7ffee43ccc80
0 @ 0x7ffee43ccc80 += 1 @ 0x7ffee43cc780
orig: 1 @ 0x7ffee43ccc80
1 @ 0x7ffee43ccc80 += 2 @ 0x2b56d095ca80
sum: 3

从原始值初始化私有副本之前将归约操作应用于原始变量。这会导致错误的结果。

您可以手动添加障碍作为解决方法:

#pragma omp parallel reduction(vec_double_plus : w)

  #pragma omp for
  for (int i = 0; i < 4; ++i)
    for (int j = 0; j < w.size(); ++j)
      w[j] += 1;
  #pragma omp barrier

【讨论】:

这是一个很好的检查方法。我的代码实际上需要性能,因此使用障碍可能不是我的最佳解决方案。我现在可能会使用 GNU,直到他们修复错误。 @Abaris 实际上你甚至不需要屏障,分割parallel / for 因为omp for 的结尾无论如何都有一个隐含的屏障。在任何情况下,这种转换都不应该对性能产生任何重大影响。 你是对的。由于 for 循环末尾的隐式屏障,它在没有屏障的情况下运行良好。现在我想更接近我想要实现的目标,但我得到了这个error。

以上是关于英特尔编译器 (C++) 问题与 std::vector 上的 OpenMP 减少有关的主要内容,如果未能解决你的问题,请参考以下文章

英特尔 C++ 编译器:最高 GCC 版本兼容性是多少?

如何打印 Vec?

在 C++ 中填充二维向量

带有 C++14 的英特尔引脚

C++ 并行化库:OpenMP 与线程构建块 [关闭]

英特尔 C++ 编译器安装程序未找到 Visual Studio