首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >openMP输出的可重现性问题

openMP输出的可重现性问题
EN

Stack Overflow用户
提问于 2019-04-09 07:59:13
回答 2查看 201关注 0票数 1

我正在学习openMP教程,并且随着我的进步,我编写了一个代码的openMP版本,它使用一个积分来计算PI。

我已经写了一个连续的版本,所以我知道串行版本是可以的。一旦openMP版本完成,我注意到每次运行它,它都会给我一个不同的答案。如果我运行几次,我可以看到输出大致在正确的数字附近,但我仍然没有预料到几个openMP运行给出了不同的答案。

代码语言:javascript
运行
AI代码解释
复制
#include<stdio.h>
#include<stdlib.h>
#include<omp.h>

void main()

{ int nb=200,i,blob;



 float summ=0,dx,argg;
 dx=1./nb;

 printf("\n dx------------: %f \n",dx);


 omp_set_num_threads(nb);
 #pragma omp parallel
 {

 blob=omp_get_num_threads();

 printf("\n we have now %d number of threads...\n",blob);

 int ID=omp_get_thread_num();
 i=ID;
 printf("\n i is now: %d \n",i);

 argg=(4./(1.+i*dx*i*dx))*dx;
 summ=summ+argg;
 printf("\t\t and summ is %f \n",summ);
 }


 printf("\ntotal summ after loop: %f\n",summ);

 }

我在RedHat上使用gcc -f mycode.c -fopenmp编译了这段代码,当我运行它时,比如说3次,我得到:

3.117

3.113

3.051

有人能帮我理解我为什么得到不同的结果吗?我做错了什么吗?并行只是分裂积分间隔,但是当矩形被计算出来时,当它们在结尾被求和时,应该是一样的,不是吗?

系列版本给了我3.13

(我没有得到3.14是正常的,因为我使用了一个非常粗糙的积分抽样,在0到1之间只有200个除法)

我也试图增加一个障碍,但我仍然得到不同的答案,虽然更接近串行版本,仍然有一个利差值和不相同的.

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-04-09 09:33:35

我认为问题在于在并行循环之外声明int ifloat argg

正在发生的情况是,所有200个线程都覆盖iargg,因此有时线程的argg会被来自另一个线程的argg覆盖,从而导致您观察到的不可预知的错误。

下面是一个工作代码,它总是打印相同的值(最多6个小数):

代码语言:javascript
运行
AI代码解释
复制
void main()
{
    int nb = 200, blob;
    float summ = 0, dx;// , argg;
    dx = 1. / nb;

    printf("\n dx------------: %f \n", dx);

    omp_set_num_threads(nb);
#pragma omp parallel
    {

        blob = omp_get_num_threads();

        printf("\n we have now %d number of threads...\n", blob);

        int i = omp_get_thread_num();
        printf("\n i is now: %d \n", i);

        float argg = (4. / (1. + i * dx*i*dx))*dx;
        summ = summ + argg;
        printf("\t\t and summ is %f \n", summ);
    }

    printf("\ntotal summ after loop: %f\n", summ);
}

但是,将最后一行更改为%.9f显示,它实际上不是完全相同的浮点数。这是由于浮点加法的数值误差造成的。a+b+c不能保证结果与a+c+b相同。可以在下面的示例中尝试这样做:

首先,在定义了并行循环( float* arr = new float[nb]; )之后,在(并行循环)和arr[i] = argg; (并行循环)之前添加arr[i] = argg;(并行循环)。然后在并行循环的之后添加以下:

代码语言:javascript
运行
AI代码解释
复制
float testSum = 0;
for (int i = 0; i < nb; i++)
    testSum += arr[i];
printf("random sum: %.9f\n", testSum);

std::sort(arr, arr + nb);
testSum = 0;

for (int i = 0; i < nb; i++)
    testSum += arr[i];
printf("sorted sum: %.9f\n", testSum);

testSum = 0;
for (int i = nb-1; i >= 0; i--)
    testSum += arr[i];
printf("reversed sum: %.9f\n", testSum);

很可能,排序和反向和略有不同,尽管它们是由完全相同的200个数字相加而成的。

您可能需要注意的另一件事是,您很难找到一个能够并行运行200个线程的处理器。大多数通用处理器可以处理4到32个线程,而专用服务器处理器可以使用$15k Xeon白金9282来处理112个线程。

因此,我们通常做以下工作:

我们删除omp_set_num_threads(nb);以使用推荐的线程数

我们从for循环中删除int i = omp_get_thread_num();以使用int i

我们将循环重写为for循环:

代码语言:javascript
运行
AI代码解释
复制
#pragma omp parallel for
for (int i = 0; i < nb; i++)
    {...}

结果应该是相同的,但是现在只使用实际硬件上可用的线程。这样可以减少线程间的上下文切换,并提高代码的时间性能。

票数 2
EN

Stack Overflow用户

发布于 2019-04-09 09:46:33

问题来自变量summarggi。它们属于全局顺序范围,如果不采取预防措施,就不能修改。您将在线程之间进行竞争,这可能会导致这些var中出现一个意外的值。种族是完全不确定的,这就解释了你得到的不同结果。您还可以获得正确的结果或任何不正确的结果,这取决于读取和写入这些vars的时间。

处理这个问题的正确方法是:

  • 对于变量arggi:它们在全局范围内声明,但它们用于在线程中执行临时计算。您应该:要么在并行域中声明它们,使它们成为线程私有,要么在omp指令中添加private(argg,i)。注意,blob也有一个潜在的问题,但是它的值在所有线程中是相同的,这不应该修改程序的行为。
  • 对于变量summ,情况则不同。这实际上是一个全局变量,它从线程中积累一些值。它必须保持全局,但在修改它时必须添加atomic openmp指令。对变量的完整读-修改-写操作将变得不可打破,这将确保无竞争修改。

下面是您的代码的修改版本,它提供了一致的结果(但是浮点数不是关联的,最后一个小数点可能会改变)。

代码语言:javascript
运行
AI代码解释
复制
#include<stdio.h>
#include<stdlib.h>
#include<omp.h>

void main()

{
  int nb=200,i,blob;
  float summ=0,dx,argg;
  dx=1./nb;

  printf("\n dx------------: %f \n",dx);

  omp_set_num_threads(nb);
# pragma omp parallel private(argg,i)
  {
    blob=omp_get_num_threads();

    printf("\n we have now %d number of threads...\n",blob);

    int ID=omp_get_thread_num();
    i=ID;
    printf("\n i is now: %d \n",i);

    argg=(4./(1.+i*dx*i*dx))*dx;
    #pragma omp atomic
    summ=summ+argg;

    printf("\t\t and summ is %f \n",summ);
  }

  printf("\ntotal summ after loop: %f\n",summ);

}

如前所述,这不是使用线程的最佳方式。创建和同步线程是昂贵的,很少需要有更多的线程数量的核心。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55596868

复制
相关文章

相似问题

领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档