【问题标题】:MPI shared memory synchronization problemMPI共享内存同步问题
【发布时间】:2019-10-22 02:12:33
【问题描述】:

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

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

我有两个问题:

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

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

我将不胜感激有关这些问题的任何帮助和建议。

【问题讨论】:

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


    【解决方案1】:

    简短说明

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

    无论如何,您不应过度使用锁定/解锁。改用冲洗。只有在分配和释放窗口时才锁定和解锁窗口。

    您可以使用 MPI 累积函数(Accumulate、Get_accumulate、Fetch_and_op、Compare_and_swap)或 - 并且仅在共享内存的情况下 - 您可以使用与编译器关联的原子原语。因为 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);
    

    【讨论】:

    • 杰夫谢谢你的回答。您能否解释一下原子操作的一部分,因为 __atomic_fetch_add() 不支持双精度(xx 数组的元素是双精度)?
    • 替换为支持双精度的适当代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 2012-05-24
    • 2012-12-31
    • 2020-10-01
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    相关资源
    最近更新 更多