c - OpenMP 不同结果 1 核与多核

标签 c multithreading openmp multicore

过去几个月我一直在尝试用 C 多核编写我的程序,但我一直遇到这个奇怪的问题。也许你们中的一些人可以帮我一点忙?

我遇到的问题是,当我让它只使用一个线程进行计算时,我的程序会给出正确的结果。然而,当我选择更多线程时,我的值开始改变,即使执行的计算应该完全相同(除了随机数生成器,但这不应该是问题,因为每个内核都有自己独特的播种器和已知生成器可与 openmp 多核处理一起使用)。

无论如何,由于程序本身是保密的,我不能给你完整的代码(反正它太大了,不方便使用)所以我会尽量给出部分代码,这可能会进一步解释问题。

首先是我包含的库:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <omp.h> /* openmp header */
#include <limits.h>
#include <float.h>
#include <gsl/gsl_rng.h> /*RNG header*/
#include <gsl/gsl_randist.h> /*RNG header*/

然后我定义了一些结构等,但我知道这些工作是因为程序确实在单核模式下工作。然后是 main 函数,其中我的 openMP 并行循环如下所示:

 //perform multicore calculations (loop over all photons)
 #pragma omp parallel for default(none) \
   num_threads(thread_cnt) \
   private(icount,thread_id,i) \
   shared(calc,imstr,sum_irefl,leaks) \
   copyin(scat,cap,pcap_ini,profile,absmu,ctvar,lib)
 for(icount=0; icount <= cap.ndet; icount++){
   thread_id = omp_get_thread_num();
   printf("\nthread %d scat:\n",thread_id);
   for(i=0;i<NDIM;i++) printf("%f\t",scat[i]);
   do{
     do{
       start(ctvar, absmu, profile, &pcap_ini, &cap, &icount, &imstr, &lib, calc, &thread_id); 
       do capil(ctvar, absmu, profile, &pcap_ini, &cap, &icount, &imstr, &lib, leaks, scat,
         calc, &thread_id);
       while (calc[thread_id].iesc == 0); /* perform capil until iesc not equal to 0 anymore */
       }
     while(calc[thread_id].iesc == -2); /* only do count etc if iesc!=-2, else redo start */
     count(absmu, ctvar, &cap, &icount, profile, leaks, &imstr,calc, &thread_id);
     }
   while(calc[thread_id].iesc == -3);
   sum_irefl[thread_id] = sum_irefl[thread_id] + calc[thread_id].i_refl;
   if(icount%1000 == 0 && thread_id == 0) printf("%d\t%ld\t%f\n",icount,calc[0].i_refl,
     calc[0].rh[2]);
   }

在这些子函数 (start, capil, count ) 中,一些变量被写入新值,calc,imstr,sum_ireflleaks 就是这种情况。我将它们设置为共享的,这样每个线程都可以访问它们。但是,我不认为存在内存竞争的机会,因为例如 calc 实际上被拆分成一个数组,其中每个线程都有自己的变量(通过它们唯一的 thread_id) 和其他共享变量可能会被争用,因为它们在任何时候都没有被读取。也许我误解了内存竞赛的危险,但我认为这不会造成问题...

copyin 变量之前是线程安全的(这里没有显示,但编译器没有提示,所以我认为这不是问题)并且它们只在并行循环期间读取,所以再次我不明白可能是什么问题。 此外,我检查了并行循环开始时的每个变量是否具有它应该具有的值。因此,当我使用 1 个或多个内核运行程序时,在并行部分的某个位置获得了不同的值。

我知道这不是很多,但我希望你们中的一些人仍然有想法。如果您需要更多信息,请随时询问我,因为我可能会提供更多信息。

所以我想知道的事情:毕竟我有可能有有害的内存竞赛吗?你看到任何其他可能出错的地方吗?您知道我可以用来检查多核程序的任何(相对)易于使用的程序吗?

最佳答案

为了扩展我的评论,代码看起来像这样。虽然线程相关值仍在循环中使用,但它们不必在每次通过时都重新初始化,这至少会为您节省一些开销。不过,我仍然不确定我从您的示例中看出问题出在哪里。所有的共享变量都只被 thread_id 引用吗?您提到它们未被读取,但它们是如何更新的,您是否可能在其中求和或以其他方式累积值?

#pragma omp parallel default(none) \
   num_threads(thread_cnt) \
   shared(calc,imstr,sum_irefl,leaks) \
   copyin(scat,cap,pcap_ini,profile,absmu,ctvar,lib)
{
 int icount, thread_id, i; //note, private by definition in the region
 thread_id = omp_get_thread_num();
 printf("\nthread %d scat:\n",thread_id);
 //perform multicore calculations (loop over all photons)
 #pragma omp for
 for(icount=0; icount <= cap.ndet; icount++){

   for(i=0;i<NDIM;i++) printf("%f\t",scat[i]);
   do{
     do{
       start(ctvar, absmu, profile, &pcap_ini, &cap, &icount, &imstr, &lib, calc, &thread_id); 
       do capil(ctvar, absmu, profile, &pcap_ini, &cap, &icount, &imstr, &lib, leaks, scat,
         calc, &thread_id);
       while (calc[thread_id].iesc == 0); /* perform capil until iesc not equal to 0 anymore */
       }
     while(calc[thread_id].iesc == -2); /* only do count etc if iesc!=-2, else redo start */
     count(absmu, ctvar, &cap, &icount, profile, leaks, &imstr,calc, &thread_id);
     }
   while(calc[thread_id].iesc == -3);
   sum_irefl[thread_id] = sum_irefl[thread_id] + calc[thread_id].i_refl;
   if(icount%1000 == 0 && thread_id == 0) printf("%d\t%ld\t%f\n",icount,calc[0].i_refl,
     calc[0].rh[2]);
   }
}

关于c - OpenMP 不同结果 1 核与多核,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17105002/

相关文章:

c - 如何在输入的同一行上打印输出?

c - 如何在我的 Eclipse 应用程序中添加鼠标悬停功能?

c++ - c++ 中的位图 - unsigned int 会完成这项工作吗?

c# - Gridview 中使用索引进行编辑

multithreading - 从后台线程更新 NSTableView 数据源

java - 从线程返回值

c# - 以线程安全的方式使用 DbContext 进行异步搜索

c - 用户定义的减少每次运行都不会返回预期结果

c++ - OpenMP:每个线程都有一个完整的 'for' 循环

linux - 如何获取 gcc OpenMP 库的调试信息(符号)?