c - 为什么使用 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
给定总和小于实际总和的原因是什么?
解决方案
变量的更新sum
不是原子操作,容易出现竞争。这种类型的比赛可能会产生小于预期的总和。
总结归结为这样的事情:
- 从内存位置加载到寄存器
- 向寄存器添加新值
- 将寄存器值存回内存
现在,当 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 将被覆盖。
您应该确保以原子方式进行求和以避免竞争。
推荐阅读
- google-apps-script - 重复功能直到满足某些条件?谷歌表格/日历 API
- r - 用 Rvest 抓取 Transfermarket
- mule - 从dataweave 2.0中的JSON消息中提取最大/最早日期
- c# - C# 项目在其他机器上部署时遇到问题
- dart - 颤振正确的 DateTime 格式以比较时间戳 Cloud Firestore 的条件
- powershell - 在 PowerShell 脚本中使用 Get-Help 时出现问题
- javascript - 悬停在图像上时显示对象数据
- django - 在 django Modelform 中以所需格式显示日期
- excel - 用保存的数据填写用户表单
- javascript - 使用ajax将javascript对象发送到php页面