2017-02-15 47 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