【问题标题】:MPI2 / MPI3: MPI_allgather vs MPI one sided communication considering synchronizationMPI2 / MPI3:考虑同步的 MPI_allgather 与 MPI 单侧通信
【发布时间】:2023-03-20 02:35:01
【问题描述】:

我正在使用 MPI-2 编写一个优化程序,其中我需要在所有进程之间共享一个相等长度的std::vectors(概念上)std::vector。该向量包含当前找到的问题的最佳k 解决方案,并且每次通过许多 MPI 进程之一找到新的最佳解决方案时都会更新。每个过程花费在寻找新解决方案上的时间通常差别很大。

我的问题是,考虑到同步和等待中的性能问题,每次找到新的最佳解决方案时,是否应该使用 MPI_allgather 等 MPI 集合;或者我应该在 MPI-2 中使用 One-Sided-Communications 来维护所有进程之间的“共享”向量。

特别是,如果我使用MPI_allgather,进程是否会提前完成其工作并等待与其他进程的某种同步?

我在 MPI 点对点通信(更新:以及 UPC)方面有一些工作经验,但在实际编码中没有使用集体或单方面的通信。我搜索了 SO 并找到了有关 MPI_allgathers 的相关问题/答案,例如Distribute a structure using MPI_Allgather ,以及关于片面沟通Creating a counter that stays synchronized across MPI processes。但是我很难说出这两种方法之间的确切区别。

谢谢,

--- 更新---

特别是,我在底部有来自Creating a counter that stays synchronized across MPI processes 的代码示例,它使用单面来维护单个int“共享”。我尝试将其调整为适用于泛型类型,但不知道如何使其工作,因为我无法理解原始代码以及它为什么维护数组data,以及如何将MPI_Accumulate 概括为用户函数(比如简单地将旧向量替换为新向量)。

template //注意:T 只能是原始类型(不能是指针、引用或结构),例如 int 和 double。 结构 mpi_array { typedef std::vector 向量; MPI_Win 获胜; int 主机等级;
国际排名;
整数大小;
向量值;
向量 *hostvals; };


单面通讯计数器代码:

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

struct mpi_counter_t {
    MPI_Win win;
    int  hostrank ;
    int  myval;
    int *data;
    int rank, size;
};

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->data));
        for (int i=0; i<count->size; i++) count->data[i] = 0;
        MPI_Win_create(count->data, count->size * sizeof(int), sizeof(int),
                       MPI_INFO_NULL, MPI_COMM_WORLD, &(count->win));
    } else {
        count->data = NULL;
        MPI_Win_create(count->data, 0, 1,
                       MPI_INFO_NULL, MPI_COMM_WORLD, &(count->win));
    }
    count -> myval = 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);
        } else {
            MPI_Get(&vals[i], 1, MPI_INT, 0, i, 1, MPI_INT, count->win);
        }
    }

    MPI_Win_unlock(0, count->win);
    count->myval += increment;

    vals[count->rank] = count->myval;
    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)->data);
    }
    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->data[i]);
        }
        puts("");
    }
}

int test1() {
    struct mpi_counter_t *c;
    int rank;
    int result;

    c = create_counter(0);

    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    result = increment_counter(c, 1);
    printf("%d got counter %d\n", rank, result);

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


int test2() {
    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);
    srandom(rank);

    while (result < WORKITEMS) {
        result = increment_counter(c, 1);
        if (result <= WORKITEMS) {
             printf("%d working on item %d...\n", rank, result);
             sleep(random() % 10);
         } else {
             printf("%d done\n", rank);
         }
    }

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

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

    MPI_Init(&argc, &argv);

    test1();
    test2();

    MPI_Finalize();
}

【问题讨论】:

  • 您不能将 MPI_Accumulate 推广到任意(即非内置)类型,因为 MPI-3 不支持(它通常称为活动消息,您可以尝试 GASNet)。

标签: c++ mpi mpi-rma


【解决方案1】:

您担心某些进程可能会在其他进程之前进入MPI_ALLGATHER,但在任何具有同步的应用程序中总是如此,而不仅仅是那些明确使用集体通信的应用程序。

但是,您似乎对片面操作的作用存在误解。他们不提供并行全局地址空间 (PGAS) 模型,其中所有内容都为您同步。相反,它们只是为您提供了一种直接寻址远程进程内存的方法。每个进程的内存仍然是独立的。此外,如果您要从点到点升级到 MPI 的其余部分,我不会将自己限制在 MPI-2 功能上。 MPI-3 中有一些新的东西也改进了集体和单方面(尤其是后者)。

话虽如此,如果您除了点对点之外从未使用过任何东西,那么单边对您来说将是一个巨大的飞跃。无论如何,您可能想要进行更多的中间步骤并首先检查集体。如果你仍然对自己的表现不满意,你可以看一下单面章节,但它非常复杂,大多数人通常最终使用位于单面之上的东西而不是直接使用它(比如可能是一些 PGAS 语言)。

【讨论】:

  • 虽然我同意你所说的大部分内容,但我认为在这种情况下使用 MPI 的 RMA 并不复杂。只需锁定窗口,访问它,然后解锁。问题是实际上要找到一个实现,该实现可能在托管窗口的进程中异步发生。至少对于 Open MPI,默认 RMA 组件仅在主机进程调用 MPI 时才接受远程操作,基本上创建了一个同步点,从而破坏了使用 RMA 的整个想法。
  • 对。要以有效的方式执行此操作,您需要使用 MPI-3 中添加的一些被动目标 RMA,这是一种完全不同的野兽。
  • 那么,是否有任何代码示例与我想要在 OP 中执行的操作接近?谢谢。
猜你喜欢
  • 2014-09-12
  • 2014-09-03
  • 1970-01-01
  • 1970-01-01
  • 2011-08-20
  • 2020-03-25
  • 1970-01-01
  • 1970-01-01
  • 2014-08-23
相关资源
最近更新 更多