如何在MPI-2 +中复制MPI_Accumulate的功能

时间:2014-07-12 04:16:27

标签: c++ mpi communication mpi-rma

我正在学习MPI-2 / MPI-3中引入的MPI单面通信,并且online course page遇到MPI_Accumulate

  

MPI_Accumulate允许调用者将移动的数据组合到   已存在数据的目标进程,例如a的累积   总结目标过程。可以通过实现相同的功能   使用MPI_Get检索数据(后跟同步);   在呼叫者处执行总和操作;然后使用MPI_Put发送   更新后的数据返回目标进程。累积简化   这种混乱......

但是,MPI_Accumulate(最大,最小,总和,产品等)只允许使用有限数量的操作,并且不允许用户定义的操作。我想知道如何使用MPI_Get,sync,op和MPI_Put来实现上述 混乱 。 C / C ++中是否有任何教程或工作代码示例?

由于


为了测试,我调整了SO question中的一段代码,其中使用单向通信来创建一个在MPI进程中保持同步的整数计数器。标记了使用MPI_Accumulate的目标问题行。

代码按原样编译并在大约15秒内返回。但是当我试图用问题行后面的注释块中显示的等效基本操作序列替换MPI_Accumulate时,编译后的程序会无限期挂起。

任何人都可以帮助解释出了什么问题,并且 在这种情况下,取代MPI_Accumulate的正确方法是什么?

P.S。我用

编译了代码
g++ -std=c++11 -I..   mpistest.cpp -lmpi

并使用

执行二进制文件
mpiexec -n 4 a.exe

代码:

//adpated from https://stackoverflow.com/questions/4948788/
#include <mpi.h>
#include <stdlib.h>
#include <stdio.h>
#include <thread>
#include <chrono>

struct mpi_counter_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  val;      
  int  *hostvals; 
};

struct mpi_counter_t *create_counter(int hostrank) {
    struct mpi_counter_t *count;

    count = (struct mpi_counter_t *)malloc(sizeof(struct mpi_counter_t));
    count->hostrank = hostrank;
    MPI_Comm_rank(MPI_COMM_WORLD, &(count->rank));
    MPI_Comm_size(MPI_COMM_WORLD, &(count->size));

    if (count->rank == hostrank) {
        MPI_Alloc_mem(count->size * sizeof(int), MPI_INFO_NULL, &(count->hostvals));
        for (int i=0; i<count->size; i++) count->hostvals[i] = 0;
        MPI_Win_create(count->hostvals, count->size * sizeof(int), sizeof(int),
                       MPI_INFO_NULL, MPI_COMM_WORLD, &(count->win));
    } 
    else {
        count->hostvals = NULL;
        MPI_Win_create(count->hostvals, 0, 1,
                       MPI_INFO_NULL, MPI_COMM_WORLD, &(count->win));
    }
    count -> val = 0;

    return count;
}

int increment_counter(struct mpi_counter_t *count, int increment) {
    int *vals = (int *)malloc( count->size * sizeof(int) );
    int val;

    MPI_Win_lock(MPI_LOCK_EXCLUSIVE, count->hostrank, 0, count->win);

    for (int i=0; i<count->size; i++) {

        if (i == count->rank) {
        MPI_Accumulate(&increment, 1, MPI_INT, 0, i, 1, MPI_INT, MPI_SUM,count->win); //Problem line: increment hostvals[i] on host
        /* //Question: How to correctly replace the above MPI_Accumulate call with the following sequence? Currently, the following causes the program to hang.
            MPI_Get(&vals[i], 1, MPI_INT, 0, i, 1, MPI_INT, count->win);
            MPI_Win_fence(0,count->win);
            vals[i] += increment;
            MPI_Put(&vals[i], 1, MPI_INT, 0, i, 1, MPI_INT, count->win);
            MPI_Win_fence(0,count->win);
        //*/
        } else {
            MPI_Get(&vals[i], 1, MPI_INT, 0, i, 1, MPI_INT, count->win);
        }
    }

    MPI_Win_unlock(0, count->win);

    //do op part of MPI_Accumulate's work on count->rank
    count->val += increment; 
    vals[count->rank] = count->val; 

    //return the sum of vals
    val = 0;
    for (int i=0; i<count->size; i++)
        val += vals[i];

    free(vals);
    return val;
}

void delete_counter(struct mpi_counter_t **count) {
    if ((*count)->rank == (*count)->hostrank) {
        MPI_Free_mem((*count)->hostvals);
    }
    MPI_Win_free(&((*count)->win));
    free((*count));
    *count = NULL;

    return;
}

void print_counter(struct mpi_counter_t *count) {
    if (count->rank == count->hostrank) {
        for (int i=0; i<count->size; i++) {
            printf("%2d ", count->hostvals[i]);
        }
        puts("");
    }
}


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

    MPI_Init(&argc, &argv);

    const int WORKITEMS=50;

    struct mpi_counter_t *c;
    int rank;
    int result = 0;

    c = create_counter(0);

    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    srand(rank);

    while (result < WORKITEMS) {
        result = increment_counter(c, 1);
        if (result <= WORKITEMS) {
             printf("%d working on item %d...\n", rank, result);
         std::this_thread::sleep_for (std::chrono::seconds(rand()%2));
         } else {
             printf("%d done\n", rank);
         }
    }

    MPI_Barrier(MPI_COMM_WORLD);
    print_counter(c);
    delete_counter(&c);


    MPI_Finalize();
    return 0;
}

还有一个问题,我应该在这里使用MPI_Win_fence而不是锁吗?

- 编辑 -

我在increment_counter中使用了锁定/解锁,如下所示,程序运行但行为异常。在最终打印输出中,主节点完成所有工作。还是很困惑。

int increment_counter(struct mpi_counter_t *count, int increment) {
    int *vals = (int *)malloc( count->size * sizeof(int) );
    int val;

    MPI_Win_lock(MPI_LOCK_EXCLUSIVE, count->hostrank, 0, count->win);

    for (int i=0; i<count->size; i++) {

        if (i == count->rank) {
            //MPI_Accumulate(&increment, 1, MPI_INT, 0, i, 1, MPI_INT, MPI_SUM,count->win); //Problem line: increment hostvals[i] on host
            ///* //Question: How to correctly replace the above MPI_Accumulate call with the following sequence? reports that 0 does all the work
            MPI_Get(&vals[i], 1, MPI_INT, 0, i, 1, MPI_INT, count->win);
            MPI_Win_unlock(0, count->win);
            vals[i] += increment;
            MPI_Put(&vals[i], 1, MPI_INT, 0, i, 1, MPI_INT, count->win);
            MPI_Win_lock(MPI_LOCK_EXCLUSIVE, count->hostrank, 0, count->win);
        //*/
        } else {
            MPI_Get(&vals[i], 1, MPI_INT, 0, i, 1, MPI_INT, count->win);
        }
    }

    MPI_Win_unlock(0, count->win);

    //do op part of MPI_Accumulate's work on count->rank
    count->val += increment; 
    vals[count->rank] = count->val; 

    //return the sum of vals
    val = 0;
    for (int i=0; i<count->size; i++)
        val += vals[i];

    free(vals);
    return val;
}

1 个答案:

答案 0 :(得分:1)

使用Get和Puts实现Accumulate确实会非常混乱,尤其是当您必须处理派生数据类型等时。但假设您正在对单个整数进行累积,并且您只想将本地值汇总到远程缓冲区中,则可以执行以下操作(仅限伪代码):

MPI_Win_lock(EXCLUSIVE);  /* exclusive needed for accumulate atomicity constraints */
MPI_Get(&remote_data);
MPI_Win_flush(win);  /* make sure GET has completed */
new = local_data + remote_data;
MPI_Put(&new);
MPI_Win_unlock();

您的代码不正确,因为您放弃了GET之后的独占锁定,这会在两个进程同时尝试对数据进行求和时导致原子性问题。