2017-02-15 7 views
2

各プロセスで文字列を生成し、すべてを収集したい。しかし、各プロセスで作成された文字列は、intとcharを付加することによって作成されます。MPI_Gatherで文字列を収集するopenmpi c

私はまだすべてを正しく収集することができません。すべての部分文字列を1つずつ印刷することができますが、rcv_stringを印刷しようとすると、部分文字列が1つだけ、またはセグメント化エラーが発生することがあります。

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の問題を回避するためには、C文字列ではなく実際のデータ({255,255,255,0}など)を中心に集めるのが一般的です。基本的なデータではなく、C文字列の通信を基本的に必要とするアプリケーションには何かがありますか? – Zulan

答えて

1

部分文字列が1つしか印刷されない誤った動作を再現することができました。

sprintfの使用に関連しています。

Cはどのようにしてcharアレイを処理しますか?

Cで配列を操作する場合、まずメモリを割り当てる必要があります。動的か静的かは関係ありません。 10 charに十分なメモリを割り当てたとします。

char my_string[10]; 

これを初期化せずに、ナンセンス文字が含まれています。

my_string"qwertyuiop"が含まれています。

my_stringに文字列fooを入力するとします。 sprintfを使用します。

sprintf(my_string, "foo"); 

Cはどのように3つの文字で10個のスロットを埋めますか?

最初の3つのスロットを3文字で埋めます。次に、4番目のスロットに「文字列の終わり」文字を入力します。これは、'\0'と表示され、コンパイラを通過するときに「文字列の終わり」文字に変換されます。

したがって、コマンドの後にmy_stringには"foo\0tyuiop"が含まれています。 my_stringを印刷すると、Cは\0の後にナンセンス文字を印刷しないことを知っています。

これはどのようにしてMPI_Gatherに関係しますか?

MPI_Gatherは、異なるプロセスからアレイを収集し、すべてを1つのプロセスで1つのアレイに配置します。

プロセス0の場合は"foo\0tyuiop"、プロセス1の場合は"bar\0ghjkl;"の場合は、"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

説明は良いですが、提案された解決法は、 'snprintf' /' strncat'とは対照的に、ハードコードされたサイズと 'sprintf' /' strcat'を使うことで悪い例になります。このコードはおそらく動作しますが、小さなものが変更されると非常にうまく失敗しません。また、[mallocの結果をキャストしない](http://stackoverflow.com/a/605858/620382)、 'abort'の代わりに' MPI_Abort'を使います。 – Zulan

+0

あなたの答えは素晴らしいです、私はあなたに答えを与えるので、私は問題を理解した。しかし、今私は別の問題を発見した...文字列に追加されたintは常に255ではない(1、20、...)ことができるので、文字列の長さが異なる可能性があります。これは、私がstrcatをprecissionで実行することができないためにメモリを予約するときに問題になります。なぜなら、正確な長さを知る必要があるからです。私は、Zulanが言っているように、snprintf/strncatを使って解決策を考えると思います。これについて私が気付かなかったコードを投稿したとき、私の間違いです。 私は今、私は別の投稿を開くことができます多くの時間がないです。とにかくありがとう! – Sergio

+0

考え方は、各プロセスに20文字などのメモリを確保することですが、プロセス0,12でプロセス1,19をプロセス3で書き込むことができます。 gathervを使用する方が良いです)、ルートプロセスですべてを収集して追加します。 ありがとう! – Sergio

-1

次のことを試してください:mpirun -n 5 ./a.outでこのコードを実行する

#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; 
} 

は、次のように生成します。

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'は正しく収集された後にnullで終了しません。 – Zulan

関連する問題