我过去几个月一直试图在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
实际上是在一个数组中拆分,其中每个线程都有自己的变量(通过它们唯一的thread_id
访问)和其他共享变量可能会被竞争,因为它们在任何时候都不会被读取。也许我误解了记忆种族的危险,但我认为这不会造成问题......
之前copyin
变量是线程安全的(这里没有显示,但编译器没有抱怨,所以我认为这不是问题)并且它们只在并行循环中被读取,所以我再次'看看问题可能是什么。
此外,我检查了并行循环开始时的每个变量都有它应该具有的值。所以在并行部分的某个地方,当我运行具有1个或更多内核的程序时,获得的值是不同的。
我知道这不是很多,但我希望你们中有些人有一个想法。如果您需要更多信息,请随时问我,因为我可以提供更多信息。
所以我想知道的事情:毕竟我有可能会有一场有害的记忆竞赛吗?你还看到其他可能出错的地方吗?你知道我可以用来检查我的多核程序的任何(相对)易于使用的程序吗?
答案 0 :(得分: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]);
}
}