Reduction Clause与Atomic Clause区别及替换后报错问题排查
OpenMP中Reduction与Atomic Clause的区别及曼德博集合程序问题分析
一、Reduction与Atomic Clause的核心区别
- Atomic Clause:针对单个内存更新操作(如
numoutside++)提供原子性保证,同一时间仅允许一个线程执行该操作,本质是细粒度锁。它只能保护单一操作,无法覆盖复杂代码块,频繁使用会带来锁竞争开销。 - Reduction Clause:为每个线程创建目标变量的私有副本,线程在并行区域内仅更新自己的私有副本,并行区域结束后,所有私有副本按指定操作(如
+)合并到全局变量。这种方式避免了频繁锁竞争,性能更优,但要求变量更新操作具备可约简性,且必须在并行区域上下文内正确访问私有副本。
二、你的曼德博程序错误原因
你用reduction替换atomic后结果出错的核心问题是:testpoint函数直接访问全局的numoutside,而非OpenMP为reduction创建的线程私有副本。
- 当你在
#pragma omp parallel for上声明reduction(+:numoutside)时,OpenMP会为每个线程生成numoutside的私有副本,初始值为0。 - 但
testpoint是独立函数,它执行numoutside++时,访问的是全局共享的numoutside变量,多个线程同时修改这个全局变量,引发严重的竞争条件,导致每次运行结果不一致。 - 而atomic版本中,
#pragma omp atomic保护了numoutside++操作,确保递增的原子性,因此结果正确。
三、修复方案
方案一:修改testpoint为返回值形式(推荐)
让testpoint返回1表示点在集合外,返回0表示在集合内,然后在并行循环中累加结果,这样线程会更新自己的私有副本:
// 修改testpoint函数 int testpoint(struct d_complex c){ struct d_complex z; int iter; double temp; z = c; for (iter = 0; iter < MAXITER; iter++){ temp = (z.r*z.r) - (z.i*z.i) + c.r; z.i = z.r*z.i*2 + c.i; z.r = temp; if ((z.r*z.r + z.i*z.i) > 4.0) { return 1; } } return 0; } // 修改并行循环部分 #pragma omp parallel for default(shared) reduction(+:numoutside) for (i=0; i<NPOINTS; i++) { struct d_complex c; double eps = 1.0e-5; for (int j=0; j<NPOINTS; j++) { c.r = -2.0+2.5*(double)(i)/(double)(NPOINTS)+eps; c.i = 1.125*(double)(j)/(double)(NPOINTS)+eps; numoutside += testpoint(c); } }
方案二:传递私有副本指针
将线程的私有numoutside副本通过指针传递给testpoint,让函数更新私有副本:
// 修改testpoint函数参数 void testpoint(struct d_complex c, int *local_num) { struct d_complex z; int iter; double temp; z = c; for (iter = 0; iter < MAXITER; iter++){ temp = (z.r*z.r) - (z.i*z.i) + c.r; z.i = z.r*z.i*2 + c.i; z.r = temp; if ((z.r*z.r + z.i*z.i) > 4.0) { (*local_num)++; break; } } } // 修改并行循环部分 #pragma omp parallel for default(shared) reduction(+:numoutside) for (i=0; i<NPOINTS; i++) { struct d_complex c; double eps = 1.0e-5; // 这里numoutside是线程私有副本 for (int j=0; j<NPOINTS; j++) { c.r = -2.0+2.5*(double)(i)/(double)(NPOINTS)+eps; c.i = 1.125*(double)(j)/(double)(NPOINTS)+eps; testpoint(c, &numoutside); } }
内容的提问来源于stack exchange,提问作者angelavor
相关产品推荐
相关产品推荐

