MPI共享内存同步问题

时间:2019-06-06 14:23:02

标签: c parallel-processing synchronization mpi shared-memory

作为MPI内存窗口共享的值的同步存在问题。使用共享内存的原因是内存结构太大,无法在每个进程上都有副本,但是需要分配其元素的计算。因此,想法是每个节点只有一个数据结构。

这是代码的简化版本,其中包含应描述问题的最小子集。我跳过了在节点之间进行同步的部分。

我有两个问题:

  1. 同步(被动目标,锁定/解锁时期)非常慢。
  2. 结果表明,历元(锁定/解锁块)内部存在一些不一致。显然,这是一个竞赛条件问题。

我尝试使用主动目标同步(MPI_Win_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);

对于这些问题的任何帮助和建议,我将不胜感激。

1 个答案:

答案 0 :(得分:0)

简短说明

MPI锁定/解锁本身不会导致原子更新。

无论如何,您不应使用过多的锁定/解锁功能。请改用冲洗。分配和释放窗口时,只能锁定和解锁窗口。

您可以使用MPI累加函数(Accumulate,Get_accumulate,Fetch_and_op,Compare_and_swap)获得原子性,或者-仅在共享内存的情况下-可以使用与编译器关联的原子基元。由于C11 / C ++ 11需要类型,因此这对于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);