C / C ++ MPI:拆分数组的更简单方法是什么

时间:2016-12-11 00:04:12

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

我是MPI的初学者,当我尝试将数组拆分成一些子数组时,我遇到了一个问题。

更确切地说,我有一个数组,让我们说int a[10]={1,3,2,7,8,12,5,7,68,10},我在X进程上运行我的程序(此时我正在使用8但可能更多或更少)。

我希望在此数组的一部分上发送给每个进程,例如,对于我的数组,此时每个进程都会收到process0 = {1, 3}process2 = {2, 7}之类的内容等等。直到{{ 1}}。

在我发送每个子阵列后,我将对每个子阵列进行一些操作,之后我想将所有子阵列合并到一个后面。

我在google上搜索了很多,我看到了一些使用process7 = 68, 10MPI_Send MPI_RecvMPI_Scatter和我的示例尝试了一些方法,但我尝试过的一切......都没有成功,我收到错误或者 空指针......

我的代码:

MPI_Gather

我曾经尝试过哪些我无法得到结果,我发表了评论,我做错了什么

非常感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

虽然dreamcrash已经建议你可以使用scatter& amp;聚集起来,我会更加重视这一点。 尽可能使用内置集合操作。不要试图自己重建它们。不仅代码更清晰,更容易理解,它也会明显更快,并允许MPI实现的各种优化。您的示例(假设gem可被N整除)变为:

size

请注意,if (rank == ROOT) { for (int i = 0; i < N; i++) { A[i] = rand() % 10; } } MPI_Scatter(A, count, MPI_INT, localArray, count, MPI_INT, ROOT, MPI_COMM_WORLD); //---------------SORT THE localArray------------------- MPI_Gather(localArray, count, MPI_INT, A, count, MPI_INT, ROOT, MPI_COMM_WORLD); MPI_Finalize(); 排名正确参与计算,并使用scatter / gather将数据发送给自身,而无需任何其他代码路径。

现在,由于您的示例明确使用ROOTN=10不能被size=8整除,因此这是一个正常运行的版本。我们的想法是在整个remainder排名中均匀分配整数除法的remainder(每个排名都需要一个额外的元素)。无论使用send / recv还是scatter / gather,你都必须这样做。使用分散/聚集,您可以使用MPI_Scatterv / MPI_Gatherv变体,这些变体采用sendcounts数组(每个等级获得多少元素)和displacements(每个地方的偏移量)全球范围内的一部分):

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

#define N 32 
int A[N]; 

int main(int argc, char *argv[]) {
    int size;
    int rank;
    const int ROOT = 0; 

    MPI_Init(&argc, &argv);

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

    // compute the work distribution
    int remainder = N % size;
    int local_counts[size], offsets[size];
    int sum = 0;
    for (int i = 0; i < size; i++) {
        local_counts[i] = N / size;
        if (remainder > 0) {
            local_counts[i] += 1;
            remainder--;
        }
        offsets[i] = sum;
        sum += local_counts[i];
    }

    int localArray[local_counts[rank]];

    if (rank == ROOT) {
        for (int i = 0; i < N; i++) {
            A[i] = rand() % 10;
        }
    }

    MPI_Scatterv(A, local_counts, offsets, MPI_INT, localArray, local_counts[rank], MPI_INT, ROOT, MPI_COMM_WORLD);

    //---------------SORT THE localArray-------------------

    MPI_Gatherv(localArray, local_counts[rank], MPI_INT, A, local_counts, offsets, MPI_INT, ROOT, MPI_COMM_WORLD);

    MPI_Finalize();
    return 0;
}

答案 1 :(得分:1)

更改您的代码:

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

#define N 32 
int A[N];  // this should be global

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

    int size;
    int rank;
    const int VERY_LARGE_INT = 999999;
    const int ROOT = 0; 
    int tag = 1234;

    MPI_Init(&argc, &argv);

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

    int count = N / size ;

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

    if (rank == ROOT) {
        for (int i = 0; i < N; i++) {
            A[i] = rand() % 10;
        }
        // master local copy
        for (int i = 0; i < count; i++)
            localArray[i] = A[i];

        for (int dest = 1; dest < size; ++dest) {
            MPI_Send(&A[dest* 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;

    for (int source = 1; source < size; source++) 
    {
        MPI_Recv(localArray, count, MPI_INT, source, 2, MPI_COMM_WORLD, 
                MPI_STATUS_IGNORE);

        //--------------------------------I CANT GET RESULT HERE-------------------------------------
        printf("Received results from task %d\n", source);
    }

    }
    else 
    {
        MPI_Recv(localArray, count, MPI_INT, ROOT, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

       //.. do something
       MPI_Send(localArray, count, MPI_INT, ROOT, 2, MPI_COMM_WORLD);
    }
    MPI_Finalize();
    return 0;
} 

一些错误:

  • 数组A是全局的,因此所有进程都拥有它,您最多 可能只想为主进程分配它;

  • 我将N / (size - 1)更改为N / size,但请注意这一点 仅在N %% size == 0时有效,因此您可能想要处理反对 场景。

  • 由于master将拥有全局数组的子副本,因此在将数据发送到从属服务器之前,我正在执行从A到本地数组的本地副本:

    // master local copy
    for (int i = 0; i < count; i++)
        localArray[i] = A[i];
    
  • 合并部分有一个小错误,主设备和从设备正在使用不同的标签,导致死锁。这就是为什么我也改变了这个原因:

    MPI_Send(localArray, count, MPI_INT, ROOT, tag, MPI_COMM_WORLD);

    MPI_Send(localArray, count, MPI_INT, ROOT, 2, MPI_COMM_WORLD);

    两者现在都有相同的标签(2);

  • 您可以使用分散和收集来实现此代码,并且可以更清晰地看到here一些示例。

另一个镜像问题是如果您使用的是C语言而不是int *localArray = (int *) malloc(count * sizeof(int));,那么您应该int *localArray = malloc(count * sizeof(int));here为什么。