【问题标题】:Splitting and Passing Array Blocks in MPI在 MPI 中拆分和传递数组块
【发布时间】:2015-11-27 23:55:48
【问题描述】:

我是 MPI 的新手,我试图通过编写一个简单的 C 程序来理解其中的含义。我要做的就是拆分一个数组并将块发送到 N 个处理器。因此,每个处理器都会在其块中找到本地分钟。然后程序(在根目录或其他地方)找到全局最小值。

我研究过MPI_SendMPI_IsendMPI_Bcast 函数,但对在哪里使用其中一个而不是另一个有点困惑。我需要一些关于我的程序的一般结构的提示:

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

#define N 9 // array size

int A[N] = {0,2,1,5,4,3,7,6,8}; // this is a dummy array

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

    int i, k = 0, size, rank, source = 0, dest = 1, count;
    int tag = 1234;

    MPI_Init(&argc, &argv);

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

    count = N/(size-1); // think size = 4 for this example

    int *tempArray = malloc(count * sizeof(int)); 
    int *localMins = malloc((size-1) * sizeof(int)); 

    if (rank == 0) {

        for(i=0; i<size; i+=count) 
        {
            // Is it better to use MPI_Isend or MPI_Bcast here?
            MPI_Send(&A[i], count, MPI_INT, dest, tag, MPI_COMM_WORLD);
            printf("P0 sent a %d elements to P%d.\n", count, dest);
            dest++;
        }
    }
    else {

        for(i=0; i<size; i+=count) 
        {       
            MPI_Recv(tempArray, count, MPI_INT, 0, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            localMins[k] = findMin(tempArray, count);
            printf("Min for P%d is %d.\n", rank, localMins[k]);
            k++;            
        }
    }

    MPI_Finalize();

    int gMin = findMin(localMins, (size-1)); // where should I assign this
    printf("Global min: %d\n", gMin); // and where should I print the results?

    return 0;
}

我的代码可能存在多个错误,很抱歉无法在此处指定确切的问题。感谢您的任何建议。

【问题讨论】:

  • 您可以使用 MPI_Scatter() 而不是拆分数组并将 MPI_Send() 用于每个进程。同样,您也可以使用MPI_Gather() 收集所有结果。
  • 查看 MAX/MIN/MAXLOC/MINLOC 减少量。
  • @VladFeinstein 谢谢,我已经阅读了该主题。但正如我所提到的,我需要一些关于我的练习的帮助才能更好地理解它。
  • @PoojaNilangekar 谢谢,我会研究这些功能并提供反馈:)

标签: c++ c arrays parallel-processing mpi


【解决方案1】:

您的代码存在几个问题(正如您已经指出的那样),并且正如一些评论者已经提到的那样,还有其他方法可以使用 MPI 调用来执行您尝试执行的操作。

但是,我将重新调整您的代码的用途,并尽量不要更改太多,以便向您展示发生了什么。

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

#define N 9 // array size
int A[N] = {0,2,1,5,4,3,7,6,8}; // this is a dummy array that should only be initialized on rank == ROOT

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

    int size;
    int rank;
    const int VERY_LARGE_INT = 999999;
    const int ROOT = 0; // the master rank that holds A to begin with
    int tag = 1234;

    MPI_Init(&argc, &argv);

    MPI_Comm_size(MPI_COMM_WORLD, &size); // think size = 4 for this example
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    /* 
       How many numbers you send from ROOT to each other rank. 
       Note that for this implementation to work, (size-1) must divide N.
    */
    int count = N/(size-1);

    int *localArray = (int *)malloc(count * sizeof(int));
    int localMin;  // minimum computed on rank i
    int globalMin; // will only be valid on rank == ROOT

    /* rank == ROOT sends portion of A to every other rank */
    if (rank == ROOT) {

        for(int dest = 1; dest < size; ++dest) 
        {
            // If you are sending information from one rank to another, you use MPI_Send or MPI_Isend.
            // If you are sending information from one rank to ALL others, then every rank must call MPI_Bcast (similar to MPI_Reduce below)
            MPI_Send(&A[(dest-1)*count], count, MPI_INT, dest, tag, MPI_COMM_WORLD);
            printf("P0 sent a %d elements to P%d.\n", count, dest);
        }
        localMin = VERY_LARGE_INT; // needed for MPI_Reduce below
    }

    /* Every other rank is receiving one message: from ROOT into local array */
    else {
        MPI_Recv(localArray, count, MPI_INT, ROOT, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        localMin = findMin(localArray, count);
        printf("Min for P%d is %d.\n", rank, localMin);
    }

    /* 
       At this point, every rank in communicator has valid information stored in localMin. 
       Use MPI_Reduce in order to find the global min among all ranks.
       Store this single globalMin on rank == ROOT.
    */
    MPI_Reduce(&localMin, &globalMin, 1, MPI_INT, MPI_MIN, ROOT, MPI_COMM_WORLD);

    if (rank == ROOT)
        printf("Global min: %d\n", globalMin);

    /* The last thing you do is Finalize MPI. Nothing should come after. */
    MPI_Finalize();
    return 0;
}

完全披露:我没有测试过这段代码,但除了小错误之外,它应该可以工作。

查看这段代码,看看你是否能理解我为什么移动你的MPI_SendMPI_Recv 电话。要理解这一点,请注意每个等级都在阅读您给它的每一行代码。因此,在您的else 语句中,不应有for 循环接收。

此外,MPI 集合(例如 MPI_ReduceMPI_Bcast)必须由通信器中的每个级别调用。这些调用的“源”和“目标”等级是函数输入参数的一部分或由集合本身隐含。

最后,给你做一点功课:你能明白为什么这不是找到数组A 的全局最小值的好实现吗?提示:rank == ROOT 完成其MPI_Sends 后在做什么?你会如何更好地分解这个问题,让每个等级的工作表现更均匀?

【讨论】:

  • 感谢您的明确答复。它确实帮助我理解了逻辑。您的代码中唯一的错误是 tempArrayelse 语句中应该是 localArray (您忘记更新变量名)。一切正常。也谢谢你的功课,我会努力的:)
  • 我想我在家庭作业中明白了 :) rank == ROOT 什么都不做!所以最好更有效地使用ROOT。也许在这种情况下使用MPI_Scatter 更好。
  • 正确!另一种看起来与您已经拥有的实现非常相似但具有更好的负载平衡的替代实现是:将问题拆分为size 部分而不是size-1 部分。然后rank == ROOT 将拥有自己的小块,它可以在发送后处理它,就像其他等级在接收后处理它们的小块一样。
  • 但是是的,基本上你是在原样重建MPI_Scatter
猜你喜欢
  • 2012-10-12
  • 2017-09-23
  • 2021-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-25
相关资源
最近更新 更多