【问题标题】:order issue in sychronizing MPI-2 one sided communication同步 MPI-2 单侧通信的顺序问题
【发布时间】:2014-09-03 20:53:51
【问题描述】:

我正在学习 MPI-2 并尝试使用 MPI-2 单面通信实现第一个简单功能:

让进程 0 托管一个固定大小的数组data_size

每个进程(包括0)都会生成一个数组并与宿主数组进行比较:

如果生成数组的第一个元素小于宿主数组的第一个元素,则将宿主数组替换为生成的数组。

在代码中:

vector<int> v1 = {rank,rank+1,rank+2};
v = get_vec(vec);
if (v1[0] < v[0])
    put_vec(vec,v1);

完整的代码在底部。当然,我希望在所有生成的数组中,具有最小头元素的数组最终应该在程序完成时出现在主机数组中,因为最小的数组(本例中为 [0,1,2])将替换其他数组并且不会自行更换。

但是,在某些(罕见的)场合,我得到了这样的输出:

$ mpiexec.exe -n 4 a.exe
#0 assigns v1 {0 ...} to host v {2 ...}
#1 assigns v1 {1 ...} to host v {2 ...}
 1  2  3

,这似乎表明同时对主机数据进行了两次分配。我想我一定是误解了get_vec/putvec 中的锁定/解锁同步指令,或者在其他地方犯了一些明显的错误。

谁能解释我应该如何修复我的代码以获得预期的输出?

提前致谢。


使用g++ -std=c++11 test.cpp -lmpi编译的完整代码:

#include <mpi.h>
#include <stdlib.h>
#include <stdio.h>
#include <thread>
#include <chrono>

#include <iostream>
#include <vector>

using namespace std;


struct mpi_vector_t {
  MPI_Win win;
  int  hostrank;  //id of the process that host values to be exposed to all processes
  int  rank;    //process id
  int  size;     //number of processes
  int  *data;
  int  data_size;
};

struct mpi_vector_t *create_vec(int hostrank, std::vector<int> v) {
    struct mpi_vector_t *vec;

    vec = (struct mpi_vector_t *)malloc(sizeof(struct mpi_vector_t));
    vec->hostrank = hostrank;
    vec->data_size = v.size();
    MPI_Comm_rank(MPI_COMM_WORLD, &(vec->rank));
    MPI_Comm_size(MPI_COMM_WORLD, &(vec->size));

    if (vec->rank == hostrank) {
        MPI_Alloc_mem(vec->data_size * sizeof(int), MPI_INFO_NULL, &(vec->data));
        for (int i=0; i<vec->size; i++) vec->data[i] = v[i];
        MPI_Win_create(vec->data, vec->data_size * sizeof(int), sizeof(int),
                       MPI_INFO_NULL, MPI_COMM_WORLD, &(vec->win));
    }
    else {
        vec->data = NULL;
        vec->data_size = v.size();
        MPI_Win_create(vec->data, 0, 1,
                       MPI_INFO_NULL, MPI_COMM_WORLD, &(vec->win));
    }
    return vec;
}

void delete_vec(struct mpi_vector_t **count) {
    if ((*count)->rank == (*count)->hostrank) {
        MPI_Free_mem((*count)->data);
    }
    MPI_Win_free(&((*count)->win));
    free((*count));
    *count = NULL;

    return;
}

std::vector<int> get_vec(struct mpi_vector_t *vec) {
    vector<int> ret(vec->data_size);
    MPI_Win_lock(MPI_LOCK_SHARED, vec->hostrank, 0, vec->win);
    MPI_Get(&ret.front(), vec->data_size, MPI_INT, vec->hostrank, 0, vec->data_size, MPI_INT, vec->win);
    MPI_Win_unlock(0, vec->win);
    return ret;
}

void put_vec(struct mpi_vector_t *vec, std::vector<int> v) {
    MPI_Win_lock(MPI_LOCK_EXCLUSIVE, vec->hostrank, 0, vec->win);
    MPI_Put(&v.front(), vec->data_size, MPI_INT, vec->hostrank, 0, vec->data_size, MPI_INT, vec->win);
    MPI_Win_unlock(0, vec->win);
}

void print_vec(struct mpi_vector_t *vec) {
    if (vec->rank == vec->hostrank) {
        for (int i=0; i<vec->data_size; i++) {
            printf("%2d ", vec->data[i]);
        }
        puts("");
    }
}


int main(int argc, char **argv) {

    MPI_Init(&argc, &argv);

    struct mpi_vector_t *vec;
    int rank;

    vector<int> v = {2,3,1};
    vec = create_vec(0, v);

    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    for (int itest = 0; itest < 2; itest++) {
        vector<int> v1 = { rank, rank + 1, rank + 2 }; //some generated data
        v = get_vec(vec);
        if (v1[0] < v[0]) {
            cout << "#" << rank << " assigns v1 {" << v1[0] <<
                    " ...} to host v {" << v[0] << " ...}" << endl;
            put_vec(vec, v1);
        }
    }

    MPI_Barrier(MPI_COMM_WORLD);
    print_vec(vec);
    delete_vec(&vec);

    MPI_Finalize();
    return 0;
}

【问题讨论】:

    标签: c++ synchronization mpi mpi-rma


    【解决方案1】:

    这是一个经典的数据竞争场景。 get_vecput_vec 都单独锁定窗口,但您实际上需要的是一个跨越整个代码块的锁,即:

    lock_window();
    v = get_vec(vec);
    if (v1[0] < v[0])
       put_vec(vec,v1);
    unlock_window();
    

    就目前而言,共享向量的内容可能会在调用get_vec() 后立即更改,因为另一个进程已执行put_vec(),这可能会使比较结果无效。像这样的:

    std::vector<int> compare_swap_vec(struct mpi_vector_t *vec, std::vector v) {
        vector<int> ret(vec->data_size);
        MPI_Win_lock(MPI_LOCK_EXCLUSIVE, vec->hostrank, 0, vec->win_ext);
        ret = get_vec(vec);
        if (v[0] < ret[0])
            put_vec(vec, v);
        MPI_Win_unlock(0, vec->win_ext);
        return ret;
    }
    

    如果小于关系成立,函数compare_swap_vec() 接受一个向量并使用它来替换共享向量的旧内容。它还返回向量的先前内容。 vec-&gt;win_ext 是托管矢量内容的同一进程托管的另一个窗口。它用于外部锁,因为 MPI 标准要求同一进程中同一窗口的不同访问时期必须是不相交的,我将其解释为不允许在同一窗口上嵌套锁。

    【讨论】:

    • 你完全正确。但是,在使用MPI_Put 中检索到的内容之前,单向通信是否特别需要在MPI_Get 调用之后进行同步操作(以等待asyc.op 完成)?或者 MPI-2 是否在某个地方说这种组合应该没问题?谢谢。
    • 虽然我关于在两次通话期间必须锁定窗口的说明是正确的,但我错过了 MPI_GetMPI_Put 都是非阻塞的并且它们保证仅在访问时期结束时完成,这意味着外部锁必须在不同的窗口上。你或许可以利用这个MPI-2 mutex implementation
    • 非常感谢您的指点。我现在对互斥锁的了解还不够,但我相信这是正确的方向。
    猜你喜欢
    • 2014-09-12
    • 2023-03-20
    • 1970-01-01
    • 2011-08-20
    • 2016-06-29
    • 2018-07-05
    • 2020-03-25
    • 2018-09-17
    • 2019-10-22
    相关资源
    最近更新 更多