跨MPI节点共享内存以防止不必要的复制

时间:2016-01-17 15:42:50

标签: c++ parallel-processing mpi

我有一个算法,在每次迭代中,每个节点都必须计算一个数组的一个部分,其中x_的每个元素都依赖于x的所有元素。

x_[i] = some_func(x) // each x_[i] depends on the entire x

也就是说,每次迭代都需要x并计算x_,这将是下一次迭代的新x

对此进行并列化的方法是MPI将在节点之间拆分x_并在计算Allgather后进行x_调用,以便每个处理器发送其{{1} }}到所有其他处理器中x_的适当位置,然后重复。这是非常低效的,因为它需要在每次迭代时进行昂贵的x调用,更不用说它需要与Allgather一样多的x副本。

我想到了一种不需要复制的替代方式。如果程序在具有共享RAM的单台机器上运行,是否可以在节点之间共享x_(无需复制)?也就是说,在计算x_之后,每个处理器将使其对其他节点可见,然后可以将其用作下一次迭代的x,而无需进行多次复制。我可以设计算法,以便没有处理器同时访问相同的x_,这就是为每个节点制作一个私有副本是多余的。

我想我要问的是:我可以简单地通过将数组标记为节点之间的共享来共享MPI中的内存,而不是手动为每个节点制作副本吗? (为简单起见假设我在一个CPU上运行)

1 个答案:

答案 0 :(得分:2)

您可以使用MPI-3中的MPI_Win_allocate_shared在节点内共享内存。它提供了一种使用Sys5和POSIX共享内存(以及类似内容)的便携方式。

MPI功能

以下摘自MPI 3.1 standard

分配共享内存

  

MPI_WIN_ALLOCATE_SHARED(size,disp_unit,info,comm,baseptr,win)
  IN局部窗口的大小(以字节为单位)(非负整数)
  IN disp_unit位移的本地单位大小,以字节为单位(正整数)
  IN info info参数(句柄)IN comm intra-communicator(句柄)
  OUT本地分配窗口段的baseptr地址(选择)
  调用返回的OUT win窗口对象(句柄)

     

int MPI_Win_allocate_shared(MPI_Aint size, int disp_unit, MPI_Info info, MPI_Comm comm, void *baseptr, MPI_Win *win)

(如果您需要Fortran声明,请单击链接)

您使用MPI_Win_free取消分配内存。分配和释放都是集体。这与Sys5或POSIX不同,但使用户界面更加简单。

查询节点分配

为了知道如何对另一个进程的内存执行加载存储,您需要在本地地址空间中查询该内存的地址。在另一个进程的地址空间中共享地址是不正确的(在某些情况下它可能有效,但不能认为它会起作用)。

  

MPI_WIN_SHARED_QUERY(win,rank,size,disp_unit,baseptr)
  IN win共享内存窗口对象(句柄)
  IN等级在窗口获胜组(非负整数)或MPI_PROC_NULL
中   窗口段的OUT大小(非负整数)
  OUT disp_unit位移的本地单元大小,以字节为单位(正整数)
  OUT baseptr地址,用于加载/存储访问窗口段(选择)

     

int MPI_Win_shared_query(MPI_Win win, int rank, MPI_Aint *size, int *disp_unit, void *baseptr)

(如果您需要Fortran声明,请单击上面的链接)

同步共享内存

  

MPI_WIN_SYNC(WIN)
  在WIN窗口对象(句柄)

     

int MPI_Win_sync(MPI_Win win)

此函数用作对与共享内存窗口关联的数据进行加载存储访问的内存屏障。

您还可以使用ISO语言功能(即C11和C ++ 11原子提供的功能)或编译器扩展(例如GCC intrinsics,例如__sync_synchronize)来获得一致的数据视图。< / p>

同步

如果您已经了解进程间共享内存语义,那么MPI-3实现将很容易理解。如果没有,请记住您需要正确同步内存和控制流。前者有MPI_Win_sync,而MPI_BarrierMPI_Send + MPI_Recv等现有MPI同步功能适用于后者。或者你可以使用MPI-3原子来构建计数器和锁。

示例程序

以下代码来自https://github.com/jeffhammond/HPCInfo/tree/master/mpi/rma/shared-memory-windows,其中包含MPI论坛用于讨论这些功能语义的共享内存使用示例程序。

该程序通过共享内存演示单向成对同步。如果您只想创建一个WORM(一次写入,多次读取)平板,那应该更简单。

#include <stdio.h>
#include <mpi.h>

/* This function synchronizes process rank i with process rank j
 * in such a way that this function returns on process rank j
 * only after it has been called on process rank i.
 *
 * No additional semantic guarantees are provided.
 *
 * The process ranks are with respect to the input communicator (comm). */

int p2p_xsync(int i, int j, MPI_Comm comm)
{
    /* Avoid deadlock. */
    if (i==j) {
        return MPI_SUCCESS;
    }

    int rank;
    MPI_Comm_rank(comm, &rank);

    int tag = 666; /* The number of the beast. */

    if (rank==i) {
        MPI_Send(NULL, 0, MPI_INT, j, tag, comm);
    } else if (rank==j) {
        MPI_Recv(NULL, 0, MPI_INT, i, tag, comm, MPI_STATUS_IGNORE);
    }

    return MPI_SUCCESS;
}

/* If val is the same at all MPI processes in comm,
 * this function returns 1, else 0. */

int coll_check_equal(int val, MPI_Comm comm)
{
    int minmax[2] = {-val,val};
    MPI_Allreduce(MPI_IN_PLACE, minmax, 2, MPI_INT, MPI_MAX, comm);
    return ((-minmax[0])==minmax[1] ? 1 : 0);
}

int main(int argc, char * argv[])
{
    MPI_Init(&argc, &argv);

    int rank, size;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    int *   shptr = NULL;
    MPI_Win shwin;
    MPI_Win_allocate_shared(rank==0 ? sizeof(int) : 0,sizeof(int),
                            MPI_INFO_NULL, MPI_COMM_WORLD,
                            &shptr, &shwin);

    /* l=local r=remote */
    MPI_Aint rsize = 0;
    int rdisp;
    int * rptr = NULL;
    int lint = -999;
    MPI_Win_shared_query(shwin, 0, &rsize, &rdisp, &rptr);
    if (rptr==NULL || rsize!=sizeof(int)) {
        printf("rptr=%p rsize=%zu \n", rptr, (size_t)rsize);
        MPI_Abort(MPI_COMM_WORLD, 1);
    }

    /*******************************************************/

    MPI_Win_lock_all(0 /* assertion */, shwin);

    if (rank==0) {
        *shptr = 42; /* Answer to the Ultimate Question of Life, The Universe, and Everything. */
        MPI_Win_sync(shwin);
    }
    for (int j=1; j<size; j++) {
        p2p_xsync(0, j, MPI_COMM_WORLD);
    }
    if (rank!=0) {
        MPI_Win_sync(shwin);
    }
    lint = *rptr;

    MPI_Win_unlock_all(shwin);

    /*******************************************************/

    if (1==coll_check_equal(lint,MPI_COMM_WORLD)) {
        if (rank==0) {
            printf("SUCCESS!\n");
        }
    } else {
        printf("rank %d: lint = %d \n", rank, lint);
    }

    MPI_Win_free(&shwin);

    MPI_Finalize();

    return 0;
}