C 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内存窗口共享的值时出现问题。使用共享内存的原因是内存结构太大,无法在每个进程上都有一个副本,但其元素的计算需要分布。所以,想法是每个节点只有一个数据结构

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

我有两个问题:

  • 同步(被动目标、锁定/解锁历元)非常慢
  • 结果表明,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的适当代码。