1

在过去的几个月里,我一直在尝试用 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_irefl情况leaks。我将它们设置为共享,因此每个线程都可以访问它们。但是,我不相信内存竞争的机会,例如calc实际上是在一个数组中拆分的,其中每个线程都有自己的变量(通过它们的 unique 访问thread_id),而其他共享变量可能会因为它们而竞争任何时候都不要阅读。也许我误解了记忆竞赛的危险,但我不认为这会造成问题......

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

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

所以我想知道的事情是:我到底有没有可能进行有害的记忆竞赛?您还看到其他可能出错的地方吗?你知道我可以用来检查我的多核程序的任何(相对)易于使用的程序吗?

4

1 回答 1

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]);
   }
}
于 2013-06-20T20:19:41.200 回答