c - 如何使用 MPI_Gather 从包括主节点在内的不同处理器收集不同长度的字符串?

标签 c arrays parallel-processing mpi

我正在尝试将来自所有处理器(包括主节点)的不同长度的不同字符串收集到主节点的单个字符串(字符数组)中。这是 MPI_Gatherv 的原型(prototype):

int MPI_Gatherv(const void *sendbuf, int sendcount, MPI_Datatype sendtype,
            void *recvbuf, const int *recvcounts, const int *displs,
            MPI_Datatype recvtype, int root, MPI_Comm comm)**.

我无法定义一些参数,例如 recvbufrecvcountsdispls。任何人都可以为此提供 C 语言的源代码示例吗?

最佳答案

正如已经指出的那样,有很多使用 MPI_Gatherv 的示例,包括此处关于堆栈溢出的示例;可以在 here 中找到一个开始描述分散和聚集如何工作,然后是 scatterv/gatherv 变体如何扩展它的答案。

至关重要的是,对于更简单的 Gather 操作,其中每个 block 的大小都相同,MPI 库可以轻松地预先计算每个 block 在最终编译数组中的位置;在更一般的 gatherv 操作中,这不太清楚,您可以选择 - 事实上,要求 - 准确说明每个项目应该从哪里开始。

这里唯一的额外复杂性是您正在处理字符串,因此您可能不希望所有东西都挤在一起;您需要额外的空格填充,当然还需要末尾的空终止符。

假设您有五个进程想要发送字符串:

Rank 0: "Hello"    (len=5)
Rank 1: "world!"   (len=6)
Rank 2: "Bonjour"  (len=7)
Rank 3: "le"       (len=2)
Rank 4: "monde!"   (len=6)

你会希望将它组装成一个全局字符串:

Hello world! Bonjour le monde!\0
          111111111122222222223
0123456789012345678901234567890

recvcounts={5,6,7,2,6};  /* just the lengths */
displs = {0,6,13,21,24}; /* cumulative sum of len+1 for padding */

可以看到位移0为0,位移i等于(recvcounts[j]+1)对于j=0..i-1的和:

   i    count[i]   count[i]+1   displ[i]   displ[i]-displ[i-1]
   ------------------------------------------------------------
   0       5          6           0    
   1       6          7           6                 6
   2       7          8          13                 7
   3       2          3          21                 8
   4       6          7          24                 3

这是直接实现的:

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

#define nstrings 5
const char *const strings[nstrings] = {"Hello","world!","Bonjour","le","monde!"};

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);

    /* Everyone gets a string */    

    int myStringNum = rank % nstrings;
    char *mystring = (char *)strings[myStringNum];
    int mylen = strlen(mystring);

    printf("Rank %d: %s\n", rank, mystring);

    /*
     * Now, we Gather the string lengths to the root process, 
     * so we can create the buffer into which we'll receive the strings
     */

    const int root = 0;
    int *recvcounts = NULL;

    /* Only root has the received data */
    if (rank == root)
        recvcounts = malloc( size * sizeof(int)) ;

    MPI_Gather(&mylen, 1, MPI_INT,
               recvcounts, 1, MPI_INT,
               root, MPI_COMM_WORLD);

    /*
     * Figure out the total length of string, 
     * and displacements for each rank 
     */

    int totlen = 0;
    int *displs = NULL;
    char *totalstring = NULL;

    if (rank == root) {
        displs = malloc( size * sizeof(int) );

        displs[0] = 0;
        totlen += recvcounts[0]+1;

        for (int i=1; i<size; i++) {
           totlen += recvcounts[i]+1;   /* plus one for space or \0 after words */
           displs[i] = displs[i-1] + recvcounts[i-1] + 1;
        }

        /* allocate string, pre-fill with spaces and null terminator */
        totalstring = malloc(totlen * sizeof(char));            
        for (int i=0; i<totlen-1; i++)
            totalstring[i] = ' ';
        totalstring[totlen-1] = '\0';
    }

    /* 
     * Now we have the receive buffer, counts, and displacements, and 
     * can gather the strings 
     */

    MPI_Gatherv(mystring, mylen, MPI_CHAR,
                totalstring, recvcounts, displs, MPI_CHAR,
                root, MPI_COMM_WORLD);


    if (rank == root) {
        printf("%d: <%s>\n", rank, totalstring);
        free(totalstring);
        free(displs);
        free(recvcounts);
    }

    MPI_Finalize();
    return 0;
}

运行给出:

$ mpicc -o gatherstring gatherstring.c -Wall -std=c99
$ mpirun -np 5 ./gatherstring
Rank 0: Hello
Rank 3: le
Rank 4: monde!
Rank 1: world!
Rank 2: Bonjour
0: <Hello world! Bonjour le monde!>

关于c - 如何使用 MPI_Gather 从包括主节点在内的不同处理器收集不同长度的字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31890523/

相关文章:

c - 我应该如何在c文件中包含用extern定义的变量

c# - Array.ConstrainedCopy() 不仅仅是备份和复制操作吗?

java - 如何在运行时输入时创建预定义大小的字符数组?

java - 在JAVA中创建更多线程来并行化线性搜索会消耗更多时间

c - OpenMP 花费的时间比预期的要长

PHP - 使用 multi_curl 进行并行处理是个好主意吗?

c - 如何打印 2 字节 unicode 字符

c - 如何将 double 组传递给 C 中的函数?

c - 运行程序时出错 - C

c++ - 为什么我的数组程序中不能使用空格?