C MPI共享内存同步问题
在同步作为MPI内存窗口共享的值时出现问题。使用共享内存的原因是内存结构太大,无法在每个进程上都有一个副本,但其元素的计算需要分布。所以,想法是每个节点只有一个数据结构 下面是代码的简化版本,其中包含描述问题的最小子集。我跳过了在节点之间进行同步的部分 我有两个问题:C MPI共享内存同步问题,c,parallel-processing,synchronization,mpi,shared-memory,C,Parallel Processing,Synchronization,Mpi,Shared Memory,在同步作为MPI内存窗口共享的值时出现问题。使用共享内存的原因是内存结构太大,无法在每个进程上都有一个副本,但其元素的计算需要分布。所以,想法是每个节点只有一个数据结构 下面是代码的简化版本,其中包含描述问题的最小子集。我跳过了在节点之间进行同步的部分 我有两个问题: 同步(被动目标、锁定/解锁历元)非常慢 结果表明,EPOCH(锁定/解锁块)内部存在一些不一致性。显然,存在一个竞赛条件问题 我尝试过使用主动目标同步(MPI\u Win\u Fence()),但也出现了同样的问题。因为我没有太多
MPI_Comm nodecomm;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, rank,
MPI_INFO_NULL, &nodecomm);
MPI_Comm_size(nodecomm, &nodesize);
MPI_Comm_rank(nodecomm, &noderank);
int local_xx_size = 0;
if (noderank == 0){
local_xx_size = xx_size;
}
MPI_Win win_xx;
MPI_Aint winsize;
double *xx, *local_xx;
MPI_Win_allocate_shared(local_xx_size*sizeof(double), sizeof(double),
MPI_INFO_NULL, nodecomm, &local_xx, &win_xx);
xx = local_xx;
if (noderank != 0){
MPI_Win_shared_query(win_xx, 0, &winsize, &windisp, &xx);
}
//init xx
if(noderank == 0){
MPI_Win_lock_all(0, win_xx);
for (i=0; i<xx_size; i++){
xx[i]=0.0;
}
MPI_Win_unlock_all(win_xx);
}
MPI_Barrier(nodecomm);
long counter = 0;
for(i = 0; i < largeNum; i++) {
//some calculations
for(j = 0; j < xx_size; j++) {
//calculate res
MPI_Win_lock_all(0, win_xx);
xx[counter] += res; //update value
MPI_Win_unlock_all(win_xx);
}
}
MPI_Barrier(nodecomm);
//use xx (sync data from all the nodes)
MPI_Win_free(&win_xx);
MPI_通信节点ecomm;
MPI通信等级(MPI通信世界和等级);
MPI通信类型(MPI通信类型世界、MPI通信类型共享、等级、,
MPI_INFO_NULL和nodecom);
MPI通信大小(节点尺寸和节点尺寸);
MPI通信等级(节点通信和节点连接);
int local_xx_size=0;
if(noderank==0){
本地_xx_大小=xx_大小;
}
MPI_Win Win_xx;
MPI_不是winsize;
双*xx,*local_xx;
MPI_Win_allocate_shared(本地_xx_大小*sizeof(双精度)、sizeof(双精度),
MPI_INFO_NULL、nodecom和local_xx和win_xx);
xx=本地_xx;
if(noderank!=0){
MPI_Win_共享_查询(Win_xx,0,&winsize,&windisp,&xx);
}
//初始化xx
if(noderank==0){
MPI_Win_lock_all(0,Win_xx);
对于(i=0;i)简单解释
MPI锁定/解锁本身不会导致原子更新
无论如何,您不应该使用超出必要的锁定/解锁。请改用flush。仅在分配和释放窗口时锁定和解锁窗口
您可以使用MPI累积函数(累积、获取累积、获取和交换、比较和交换)获得原子性或者——并且仅在共享内存的情况下——您可以使用与编译器相关联的原子原语。因为C11/C++11需要类型,所以这有点棘手,我在下面的示例中展示了大多数(如果不是所有)通用编译器都假设的内部函数
代码更改建议
我不知道这是否正确,它只是说明了上述概念
MPI_Comm nodecomm;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, rank,
MPI_INFO_NULL, &nodecomm);
MPI_Comm_size(nodecomm, &nodesize);
MPI_Comm_rank(nodecomm, &noderank);
int local_xx_size = 0;
if (noderank == 0){
local_xx_size = xx_size;
}
MPI_Win win_xx;
MPI_Aint winsize;
double *xx, *local_xx;
MPI_Win_allocate_shared(local_xx_size*sizeof(double), sizeof(double), MPI_INFO_NULL, nodecomm, &local_xx, &win_xx);
MPI_Win_lock_all(0, win_xx);
xx = local_xx;
if (noderank != 0){
MPI_Win_shared_query(win_xx, 0, &winsize, &windisp, &xx);
}
//init xx
if(noderank == 0){
for (i=0; i<xx_size; i++){
xx[i]=0.0;
}
}
MPI_Barrier(nodecomm);
long counter = 0;
for(i = 0; i < largeNum; i++) {
//some calculations
for(j = 0; j < xx_size; j++) {
//calculate res
// xx[counter] += res; //update value
#ifdef USE_RMA_ATOMICS
// check the arguments - I don't know if I calculate the target+displacement right
int target = counter/local_xx_size;
MPI_Aint disp = counter%local_xx_size;
MPI_Accumulate(&res, MPI_LONG, target, disp, 1, MPI_LONG, MPI_SUM, win_xx);
MPI_Win_flush(target, win_xx);
#else
# ifdef USE_NEWER_INTRINSICS // GCC, Clang, Intel support this AFAIK
__atomic_fetch_add (&xx[counter], res, __ATOMIC_RELAXED);
# else // GCC, Clang, Intel, IBM support this AFAIK
__sync_fetch_and_add(&xx[counter], res);
# endof
#endif
}
}
MPI_Barrier(nodecomm);
MPI_Win_unlock_all(win_xx);
MPI_Win_free(&win_xx);
MPI_通信节点ecomm;
MPI通信等级(MPI通信世界和等级);
MPI通信类型(MPI通信类型世界、MPI通信类型共享、等级、,
MPI_INFO_NULL和nodecom);
MPI通信大小(节点尺寸和节点尺寸);
MPI通信等级(节点通信和节点连接);
int local_xx_size=0;
if(noderank==0){
本地_xx_大小=xx_大小;
}
MPI_Win Win_xx;
MPI_不是winsize;
双*xx,*local_xx;
MPI_Win_allocate_shared(本地_xx_size*sizeof(double)、sizeof(double)、MPI_INFO_NULL、nodecom和本地_xx以及Win_xx);
MPI_Win_lock_all(0,Win_xx);
xx=本地_xx;
if(noderank!=0){
MPI_Win_共享_查询(Win_xx,0,&winsize,&windisp,&xx);
}
//初始化xx
if(noderank==0){
对于(i=0;iJeff)谢谢您的回答。请您解释原子操作的部分,因为_atomic_fetch_add()不支持double(xx数组的元素是double)?请替换为支持double的适当代码。