openmp/C++ 简单并行区域返回不一致的结果

Posted

技术标签:

【中文标题】openmp/C++ 简单并行区域返回不一致的结果【英文标题】:openmp/C++ simple parallel region returns incosistent results 【发布时间】:2016-06-04 20:29:40 【问题描述】:

所以我正在尝试学习 OpenMP API,我已经掌握了基础知识,但这部分让我感到困惑:(可测试的代码!)

#include <iostream>
#include <cstdlib>
#include <string>
#include <omp.h>
#include <unistd.h>

using namespace std;
const int col = 10, row = 10;
int c[][11] = -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1  ;

int main(int argc, char** argv) 

int temp[3] = -1, -1, -1;
int id;
for (unsigned short i = 0; i < 10; i++)
    #pragma omp parallel shared(c) firstprivate(i) private(temp)  
    
        #pragma ivdep
        #pragma omp for schedule(static, 1) //private(id)//,m_i, ind)
        for(unsigned short j = 0; j < 10; j++)
            // calculate c
            /*  old
            temp[0] = c[i-1][j-1]+3;
            temp[1] = c[i-1][j]-4;
            temp[2] = c[i][j-1]-5;
            c[i][j] = temp[0]; 
            c[i][j] = temp[1];
            c[i][j] = temp[2];
            */
            temp[0] = c[i][j-1]+3;
            c[i][j] = temp[0]; 
        
    

// print c
for (int i = 0; i< row + 1; i++)

    for (int j = 0; j< col + 1; j++)
    
        cout << c[i][j] << "/";
    
    cout << endl;

  // end main

我相信代码是自我解释的,我只想补充一点,我只想并行化内部循环,因为我希望外部循环与 c 数组中的依赖项一起使用。

还可以使用rowcol 变量来控制数组的维度。

我使用这个 bash 脚本来运行整个程序

#!/bin/bash
g++ -O0 -o xx par_test.cpp -fopenmp
export OMP_NUM_THREADS=1 # or 2 or 4
./xx

但是当我使用多个 1 线程时,输出会有所不同。 OMP_NUM_THREADS=1 的输出(一致)

OMP_NUM_THREADS=2 的输出(每次执行都会返回不同的内容)

OMP_NUM_THREADS=4 的输出(每次执行都返回不同的东西)

我也尝试过的事情:

带 & 不带矢量化(即带 & 不带 #pragma ivdep) threadprivate(temp) 这里也没有运气。

由于单线程执行总是具有相同的输出,我相当确定我的变量共享存在问题,尽管它让我无法理解..

提前感谢您的宝贵时间。

【问题讨论】:

您正在使用 openmp 和 simd 指令。这可能会出错。尝试删除#pragma ivdep @hr0m 根据我的理解,#pragma ivdep 向编译器建议下一个循环可以向量化,不是吗?因此,如果我将其删除,则不会发生矢量化(即已经这样做了)。无论如何,正如我上面提到的,我已经尝试过使用和不使用这个特定的#pragma,但行为保持不变 - 顺便说一句,代码是可测试的。 omfg 我真是瞎了眼!!!您在循环本身中有依赖关系!!!我不知道你为什么使用临时工。如果您正在更改 c[i][j],它可能已经在上一步中更改。记住你是平行的! 并行化外部循环,而不是内部循环,因为循环携带的依赖关系。 【参考方案1】:

您正在此处对j 进行并行循环:

    #pragma omp for schedule(static, 1)
    for(unsigned short j = 0; j < 10; j++)
        // calculate c
        temp[0] = c[i][j-1]+3;
        c[i][j] = temp[0]; 
    

您引用了单元格c[i][j-1],但该值可能正在另一个线程中计算。你怎么能确定这已经发生了?

【讨论】:

哦,现在我明白了。所以基本上,计算将是正确的,只要我不使用在同一个 j 循环中计算的c 单元格(即,如果我使用 @ 987654325@ 而不是 c[i][j-1] 都可以吗?) 是的,这是正确的,因为循环中没有改变。 或者即使我使用了c 的固定单元格(例如c[0][0] ),对于计算,它仍然应该正确计算吗?那么,我只需要调节对单元格的写入而不是读取? 如果您使用这些值进行计算,您必须考虑在循环中更改了哪些值。如果您的一个线程更改c[0][0] 而其他线程之一使用它进行计算,那么您就有麻烦了。

以上是关于openmp/C++ 简单并行区域返回不一致的结果的主要内容,如果未能解决你的问题,请参考以下文章

OpenMP/C++:并行 for 循环,之后减少 - 最佳实践?

我要求我的并行 OpenMP C 代码的执行时间解决方案

如何并行进行多个 Spring Webclient 调用并等待结果?

如何通过并行处理数据库结果来提高性能?

omp_get_max_threads() 在并行区域返回 1,但应该是 8

为啥 XmlNamespaceManager 为 HasNamespace 返回不一致的结果?