首页 > 解决方案 > 为什么使用 OpenMP 时数组总和小于实际总和?

问题描述

我用 C 语言编写了以下程序,使用 OpenMP 库进行并行编程,以找到大小为 10000000 的数组的总和。预期的输出应该是元素总和 = 10000000,但我得到的输出小于总和。

#include <stdio.h>
#define ARR_SIZE 10000000
int a[ARR_SIZE];
int main(int argc, char* argv[])
{
 int i,tid,numt;
int sum=0;
double t1,t2;
for(i=0;i<ARR_SIZE;i++)
a[i]=1;

t1=omp_get_wtime();

#pragma omp parallel default(shared) private(i,tid)
{
int from,to;
tid=omp_get_thread_num();
numt=omp_get_num_threads();

from = (ARR_SIZE/numt)*tid;
to= (ARR_SIZE/numt)*(tid+1)-1;

if(tid == numt-1)
to= ARR_SIZE-1;

printf("Hello from %d of %d , my range is from = %d to %d \n",tid,numt,from,to);

for(i=from;i<=to;i++)
sum+=a[i];
}

t2=omp_get_wtime();

printf("Sum of the array elements = %d time = %g \n",sum,t2-t1);
return 0;
}

一些示例输出是:

输出 1

你好,从 0 of 4 开始,我的范围从 = 0 到 2499999
你好,从 3 of 4 开始,我的范围从 = 7500000 到 9999999
你好,从 1 of 4 开始,我的范围从 = 2500000 到 4999999
你好,从 2 of 4 ,我的范围从 = 5000000 到 7499999
数组元素之和 = 3235618 时间 = 0.118754

输出 2

你好,从 3 of 4 开始,我的范围从 = 7500000 到 9999999
你好,从 0 of 4 开始,我的范围从 = 0 到 2499999
你好,从 2 of 4 开始,我的范围从 = 5000000 到 7499999
你好,从 1 of 4 ,我的范围从 = 2500000 到 4999999
数组元素之和 = 2964874 时间 = 0.129216

给定总和小于实际总和的原因是什么?

标签: cparallel-processingopenmp

解决方案


变量的更新sum不是原子操作,容易出现竞争。这种类型的比赛可能会产生小于预期的总和。

总结归结为这样的事情:

  1. 从内存位置加载到寄存器
  2. 向寄存器添加新值
  3. 将寄存器值存回内存

现在,当 4 个线程执行上述操作而不考虑其他线程时,一些加法将丢失,导致总和低于预期。

例如,使用 2 个线程(为简单起见):

Thread 1: Load to a register from memory location
Thread 2: Load to a register from memory location
Thread 1: Add new value to the register
Thread 2: Add new value to the register
Thread 1: Store the register value back to the memory
Thread 2: Store the register value back to the memory

在此示例中,最后添加的线程 1 将被覆盖。

您应该确保以原子方式进行求和以避免竞争。


推荐阅读