2017-02-15 38 views
2

我想为每个进程生成一个字符串,然后收集所有内容。但是在每个过程中创建的字符串都是通过追加int和chars来创建的。用MPI_Gather收集字符串openmpi c

我仍然无法正确收集所有内容。我可以逐个打印所有部分字符串,但是如果我尝试打印rcv_string,我只会得到一个部分字符串或者可能是分段错误。

我已经尝试在memset的字符串的末尾放置零,动态和静态地为字符串保留内存......但我没有找到方法。

如果有人知道如何初步化弦并为实现目标进行适当聚拢,那将会很棒。

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

    int rank; 
    MPI_Init(&argc, &argv); 
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); 

    char *string;  // ???????????? 
    char *rcv_string; // ???????????? 

    if (rank == 0) { 
     sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0); 
    } 
    else if (rank == 1) { 
     sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0); 
    } 
    else if (rank == 2) { 
     sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0); 
    } 
    else if (rank == 3) { 
     sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0); 
    } 
    else if (rank == 4) { 
     sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0); 
    } 
    else if (rank == 5) { 
     sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0); 
    } 

    MPI_Gather(string,???,MPI_CHAR,rcv_string,???,MPI_CHAR,0,MPI_COMM_WORLD); 

    if (rank == 0) { 
     printf("%s",rcv_string); 
    } 

    MPI_Finalize(); 
    return 0; 
} 
+0

为了避免XY-问题:一般来说,要收集实际数据(例如{255,255,255,0})而不是C字符串要简单得多。您的应用程序中是否有任何基本要求您交流C字符串而非底层数据的内容? – Zulan

回答

1

我设法重现不正确的行为,只打印一个部分字符串。

这与您使用sprintf有关。

C如何处理char数组?

在C中使用数组时,必须先为它分配内存。动态还是静态,这并不重要。假设您为10 char s分配足够的内存。

char my_string[10]; 

没有初始化它,它包含无意义的字符。

让我们假装my_string包含"qwertyuiop"

假设您想填充my_string字符串foo。您使用sprintf

sprintf(my_string, "foo"); 

C如何填充10个带3个字符的插槽?

它用3个字符填充前3个插槽。然后,它用“字符串结束”字符填充第4个插槽。这表示为'\0',它在通过编译器时转换为“字符串结尾”字符。

因此,在您的命令后,my_string包含"foo\0tyuiop"。如果您打印出my_string,C知道不要在\0之后打印出无意义的字符。

这与MPI_Gather有什么关系?

MPI_Gather从不同进程收集数组,并将它们全部放入一个进程中的一个数组中。

如果您在过程1上有过程0和"bar\0ghjkl;"上的"foo\0tyuiop",则它们合并到"foo\0tyuiopbar\0ghjkl;"中。

正如你所看到的,来自进程1的数组出现在进程0的“行尾”字符之后.C将把来自进程1的所有字符看作是无意义的。

修修补补解决

,而不是试图一次打印全部rcv_string,承认,在整个散“的字符串的结束”字符。然后,根据其来自的过程,打印出具有不同“字符串起始位置”的字符串。

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

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

    int rank, size; 
    MPI_Init(&argc, &argv); 
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); 
    MPI_Comm_size(MPI_COMM_WORLD, &size); 

    int part_str_len = 18; 

    char *my_string; 
    char *rcv_string; 

    if ((my_string = malloc(part_str_len*sizeof(char))) == NULL){ 
    MPI_Abort(MPI_COMM_WORLD,1); 
    } 
    if ((rcv_string = malloc(part_str_len*size*sizeof(char))) == NULL){ 
    MPI_Abort(MPI_COMM_WORLD,1); 
    } 

    sprintf(my_string, "%dr%dg%db%dl\n",255,255,255,0); 

    MPI_Gather(my_string,18,MPI_CHAR,rcv_string,18,MPI_CHAR,0,MPI_COMM_WORLD); 

    if (rank == 0) { 
    printf("%s",rcv_string); 
    } 

    char *cat_string; 
    if ((cat_string = malloc(part_str_len*size*sizeof(char))) == NULL){ 
    MPI_Abort(MPI_COMM_WORLD,1); 
    } 

    if (rank == 0){ 
    int i; 
    sprintf(cat_string, "%s", rcv_string); 
    for (i = 1; i < size; i++){ 
     strcat(cat_string, &rcv_string[part_str_len*i]); 
    } 
    } 

    if (rank == 0) { 
    printf("%s",cat_string); 
    } 

    free(my_string); 
    free(rcv_string); 
    free(cat_string); 

    MPI_Finalize(); 
    return 0; 
} 
+2

解释很好,但是建议的解决方案通过使用硬编码大小和'sprintf' /'strcat'而不是'snprintf' /'strncat'来设置一个坏例子。该代码可能会按原样工作,但一旦小的事情发生变化就会失败。另外:[不要转换'malloc'](http://stackoverflow.com/a/605858/620382)的结果,使用'MPI_Abort'而不是'abort'。 – Zulan

+0

你的回答很好,我明白了这个问题,所以我给你答案。但是现在我发现了另一个问题...附加到字符串的整数不总是255(可能是1,20,...),所以字符串的长度可能不同。当我们为它保留内存时,这是一个问题,因为我不能准确地进行strcat,因为我需要知道确切的长度。 Zulan说,我认为解决方案是通过使用snprintf/strncat来实现的。当我发布代码时,我没有意识到这一点,是我的错误。 现在我没有太多时间,只要我可以打开另一篇文章。不管怎么说,还是要谢谢你! – Sergio

+0

这个想法是保留内存,例如每个进程20个字符,但我可以只写入5与进程0,12与进程1,19与进程3,... 然后做一个聚会(也许使用gatherv会更好),并收集所有内容并将其追加到根进程中。 谢谢! – Sergio

-1

尝试以下操作:

#define MAX_STR_LEN 100 

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

    int rank, size; 
    MPI_Init(&argc, &argv); 
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); 
    MPI_Comm_size(MPI_COMM_WORLD, &size); 

    char string[MAX_STR_LEN] = "some string"; 

    char *rcv_string = NULL; 
    if (rank == 0) { 
     // Only the master needs to allocate the memory 
     // for the result string which needs to be large 
     // enough to contain the input strings from `size` 
     // peers. 
     rcv_string = malloc(MAX_STR_LEN * size); 
    } 

    ...same code... 

    MPI_Gather(string, strlen(string), MPI_CHAR, 
       rcv_string, MAX_STR_LEN, MPI_CHAR, 0, MPI_COMM_WORLD); 

    if (rank == 0) { 
     printf("%s",rcv_string); 
     free(rcv_string); 
    } 

    MPI_Finalize(); 
    return 0; 
} 

mpirun -n 5 ./a.out运行这段代码将产生以下:

some string255r255g255b0l 
some string255r255g255b0l 
some string255r255g255b0l 
some string255r255g255b0l 
some string255r255g255b0l 

确保定义MAX_STR_LEN所以这是对你的要求不够大。如果值变大,则可能需要考虑堆分配(即malloc)。

+0

这不起作用。使用'MAX_STR_LEN'作为''recvcount'为'MPI_Gather'将使所有字符串分开,分别开始'MAX_STR_LEN',其间有未初始化的值。在收集之后,'rcv_string'也不是正确的空终止。 – Zulan