使用OpenMP Task并行化Pi计算程序后结果为0的问题排查
OpenMP递归计算Pi并行程序错误排查
问题描述
我尝试用OpenMP task将递归计算Pi的串行程序改为并行版本,串行程序能得到正确的Pi值(3.141593),但并行版本运行结果Pi为0.000000,无法得到正确结果。
串行代码
#include <omp.h> #include <stdio.h> static long num_steps = 1024*1024*1024; #define MIN_BLK 1024*1024*256 double pi_comp(int Nstart,int Nfinish,double step) { int i,iblk; double x, sum = 0.0,sum1, sum2; if (Nfinish-Nstart < MIN_BLK){ for (i=Nstart;i< Nfinish; i++){ x = (i+0.5)*step; sum = sum + 4.0/(1.0+x*x); } } else{ iblk = Nfinish-Nstart; sum1 = pi_comp(Nstart, Nfinish-iblk/2,step); sum2 = pi_comp(Nfinish-iblk/2, Nfinish, step); sum = sum1 + sum2; }return sum; } int main () { int i; double step, pi, sum; double init_time, final_time; step = 1.0/(double) num_steps; init_time = omp_get_wtime(); sum = pi_comp(0,num_steps,step); pi = step * sum; final_time = omp_get_wtime() - init_time; printf(" for %ld steps pi = %f in %f secs\n",num_steps,pi,final_time); }
并行代码(错误版本)
#include <omp.h> #include <stdio.h> static long num_steps = 1024*1024*1024; #define MIN_BLK 1024*1024*256 double pi_comp(int Nstart,int Nfinish,double step) { int i,iblk; double x, sum = 0.0,sum1, sum2; if (Nfinish-Nstart < MIN_BLK){ for (i=Nstart;i< Nfinish; i++){ x = (i+0.5)*step; sum = sum + 4.0/(1.0+x*x); } } else{ iblk = Nfinish-Nstart; #pragma omp task firstprivate(sum1) sum1 = pi_comp(Nstart,Nfinish-iblk/2,step); #pragma omp task firstprivate(sum2) sum2 = pi_comp(Nfinish-iblk/2, Nfinish,step); sum = sum1 + sum2; }return sum; } int main () { int i; double step, pi, sum; double init_time, final_time; step = 1.0/(double) num_steps; init_time = omp_get_wtime(); #pragma omp parallel { #pragma omp single sum = pi_comp(0,num_steps,step); } pi = step * sum; final_time = omp_get_wtime() - init_time; printf(" for %ld steps pi = %f in %f secs\n",num_steps,pi,final_time); }
运行输出
g++ -fopenmp -c pi_recur_omp.cpp g++ -fopenmp -o pi_recur_omp pi_recur_omp.o -lm ./pi_recur for 1073741824 steps pi = 3.141593 in 4.562793 secs ./pi_recur_omp for 1073741824 steps pi = 0.000000 in 2.990318 secs
错误原因分析
核心问题是没有等待异步任务完成就读取计算结果:
- OpenMP task是异步执行的,创建
sum1和sum2的任务后,当前线程会直接执行sum = sum1 + sum2,但此时两个任务还未完成,sum1和sum2仍为初始的0.0,导致最终sum为0,Pi计算结果自然是0。 - 额外问题:
firstprivate(sum1)和firstprivate(sum2)是错误的,sum1、sum2是当前函数的局部变量,firstprivate会创建它们的私有副本,任务中赋值的是副本而非原变量,原变量依然保持0值。
修正后的并行代码
#include <omp.h> #include <stdio.h> static long num_steps = 1024*1024*1024; #define MIN_BLK 1024*1024*256 double pi_comp(int Nstart,int Nfinish,double step) { int i, iblk; double x, sum = 0.0, sum1, sum2; if (Nfinish - Nstart < MIN_BLK) { for (i = Nstart; i < Nfinish; i++) { x = (i + 0.5) * step; sum += 4.0 / (1.0 + x*x); } } else { iblk = Nfinish - Nstart; // 创建任务,默认shared属性,直接修改原变量 #pragma omp task shared(sum1) sum1 = pi_comp(Nstart, Nfinish - iblk/2, step); #pragma omp task shared(sum2) sum2 = pi_comp(Nfinish - iblk/2, Nfinish, step); // 等待所有任务完成后再计算总和 #pragma omp taskwait sum = sum1 + sum2; } return sum; } int main () { int i; double step, pi, sum; double init_time, final_time; step = 1.0 / (double) num_steps; init_time = omp_get_wtime(); #pragma omp parallel { #pragma omp single sum = pi_comp(0, num_steps, step); } pi = step * sum; final_time = omp_get_wtime() - init_time; printf(" for %ld steps pi = %f in %f secs\n", num_steps, pi, final_time); }
修正说明
- 添加
#pragma omp taskwait:强制当前线程等待所有已创建的任务完成,确保sum1和sum2已经被正确赋值后再计算总和。 - 移除
firstprivate:改用默认的shared属性,让任务直接修改原函数的sum1和sum2变量,确保赋值结果能被主线程读取。
内容的提问来源于stack exchange,提问作者HotbrewGX
相关产品推荐
相关产品推荐

