2012-05-02 15 views
3

私はMPI_Datatypeを定義し、MPI_Gathervと組み合わせて使用​​する方法を学ぶための簡単なコードを書いています。私は、MPI_Finalize()を呼び出すまで、可変長の動的に割り当てられた構造化データの配列をプロセス上で組み合わせて、うまく動作しているように見せたいと思っていました。私はこれが、print文とEclipse PTPデバッガ(バックエンドはgdb-mi)を使用することで問題が明らかになる場所であることを確認しました。私の主な質問は、どうやってセグメンテーション違反を取り除くことができるかということです。C、Open MPI:MPI_Finalize()へのコールからのセグメンテーションフォルト。特に少数のプロセスでSegfaultが起こるとは限らない

コードを実行するたびにsegfaultは発生しません。たとえば、2つまたは3つのプロセスでは発生していませんが、4つ以上のプロセスで実行すると定期的に発生する傾向があります。

また、valgrindでこのコードを実行すると、セグメンテーションフォルトは発生しません。しかし、私はvalgrindからエラーメッセージを受け取りますが、目標とする抑制が多数ある場合でも、MPI関数を使用すると出力がわかりにくいです。私はまた、私がより多くの抑止を使用する場合、私は有用なエラーメッセージを黙らせることに懸念しています。

Iは、これらのフラグを使用して、通常のコードをコンパイルするので、私は両方のケースでC99標準を使用しています: -ansi -pedantic -Wall -O2 -march =バルセロナ-fomitフレームポインタ-std = C99 と -ansi -pedantic -std = c99 -Wall -g

両方ともgcc 4.4 mpiccコンパイラを使用し、Red Hat LinuxをOpen MPI v1.4.5で使用してクラスタで実行します。他の重要な情報を除外した場合は、私にお知らせください。ここにコードがあり、事前に感謝しています:

//#include <unistd.h> 
#include <string.h> 
#include <stdio.h> 
#include <math.h> 
#include <stdlib.h> 
//#include <limits.h> 

#include "mpi.h" 

#define FULL_PROGRAM  1 

struct CD{ 

    int int_ID; 
    double dbl_ID; 
}; 

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

    int numprocs, myid, ERRORCODE; 

#if FULL_PROGRAM 
    struct CD *myData=NULL;    //Each process contributes an array of data, comprised of 'struct CD' elements 
    struct CD *allData=NULL;   //root will dynamically allocate this array to store all the data from rest of the processes 
    int *p_lens=NULL, *p_disp=NULL;  //p_lens stores the number of elements in each process' array, p_disp stores the displacements in bytes 
    int MPI_CD_size;     //stores the size of the MPI_Datatype that is defined to allow communication operations using 'struct CD' elements 

    int mylen, total_len=0;    //mylen should be the length of each process' array 
             //MAXlen is the maximum allowable array length 
             //total_len will be the sum of mylen across all processes 

    // ============ variables related to defining new MPI_Datatype at runtime ==================================================== 
    struct CD sampleCD = {.int_ID=0, .dbl_ID=0.0}; 
    int blocklengths[2];    //this describes how many blocks of identical data types will be in the new MPI_Datatype 
    MPI_Aint offsets[2];    //this stores the offsets, in bytes(bits?), of the blocks from the 'start' of the datatype 
    MPI_Datatype block_types[2];  //this stores which built-in data types the blocks are comprised of 
    MPI_Datatype myMPI_CD;    //just the name of the new datatype 
    MPI_Aint myStruct_address, int_ID_address, dbl_ID_address, int_offset, dbl_offset; //useful place holders for filling the arrays above 
    // =========================================================================================================================== 
#endif 
    // =================== Initializing MPI functionality ============================ 
    MPI_Init(&argc, &argv); 
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs); 
    MPI_Comm_rank(MPI_COMM_WORLD, &myid); 
    // =============================================================================== 
#if FULL_PROGRAM 
    // ================== This part actually formally defines the MPI datatype =============================================== 
    MPI_Get_address(&sampleCD, &myStruct_address);   //starting point of struct CD 
    MPI_Get_address(&sampleCD.int_ID, &int_ID_address);  //starting point of first entry in CD 
    MPI_Get_address(&sampleCD.dbl_ID, &dbl_ID_address);  //starting point of second entry in CD 
    int_offset = int_ID_address - myStruct_address;   //offset from start of first to start of CD 
    dbl_offset = dbl_ID_address - myStruct_address;   //offset from start of second to start of CD 

    blocklengths[0]=1; blocklengths[1]=1;     //array telling it how many blocks of identical data types there are, and the number of entries in each block 
    //This says there are two blocks of identical data-types, and both blocks have only one variable in them 

    offsets[0]=int_offset; offsets[1]=dbl_offset;   //the first block starts at int_offset, the second block starts at dbl_offset (from 'myData_address' 

    block_types[0]=MPI_INT; block_types[1]=MPI_DOUBLE;  //the first block contains MPI_INT, the second contains MPI_DOUBLE 

    MPI_Type_create_struct(2, blocklengths, offsets, block_types, &myMPI_CD);  //this uses the above arrays to define the MPI_Datatype...an MPI-2 function 

    MPI_Type_commit(&myMPI_CD);  //this is the final step to defining/reserving the data type 
    // ======================================================================================================================== 

    mylen = myid*2;  //each process is told how long its array should be...I used to define that randomly but that just makes things messier 

    p_lens = (int*)  calloc((size_t)numprocs, sizeof(int));  //allocate memory for the number of elements (p_lens) and offsets from the start of the recv buffer(d_disp) 
    p_disp = (int*)  calloc((size_t)numprocs, sizeof(int)); 

    myData = (struct CD*) calloc((size_t)mylen,  sizeof(struct CD));   //allocate memory for each process' array 
    //if mylen==0, 'a unique pointer to the heap is returned' 

    if(!p_lens) { MPI_Abort(MPI_COMM_WORLD, 1); exit(EXIT_FAILURE); } 
    if(!p_disp) { MPI_Abort(MPI_COMM_WORLD, 1); exit(EXIT_FAILURE); } 
    if(!myData) { MPI_Abort(MPI_COMM_WORLD, 1); exit(EXIT_FAILURE); } 


    for(double temp=0.0;temp<1e6;++temp) temp += exp(-10.0); 
    MPI_Barrier(MPI_COMM_WORLD);        //purely for keeping the output organized by give a delay in time 

    for (int k=0; k<numprocs; ++k) { 

     if(myid==k) { 

      //printf("\t ID %d has %d entries: { ", myid, mylen); 

      for(int i=0; i<mylen; ++i) { 

       myData[i]= (struct CD) {.int_ID=myid*(i+1), .dbl_ID=myid*(i+1)};   //fills data elements with simple pattern 
       //printf("%d: (%d,%lg) ", i, myData[i].int_ID, myData[i].dbl_ID); 
      } 
      //printf("}\n"); 
     } 
    } 

    for(double temp=0.0;temp<1e6;++temp) temp += exp(-10.0); 
    MPI_Barrier(MPI_COMM_WORLD);       //purely for keeping the output organized by give a delay in time 

    MPI_Gather(&mylen, 1, MPI_INT, p_lens, 1, MPI_INT, 0, MPI_COMM_WORLD);  //Each process sends root the length of the vector they'll be sending 

#if 1 
    MPI_Type_size(myMPI_CD, &MPI_CD_size);   //gets the size of the MPI_Datatype for p_disp 
#else 
    MPI_CD_size = sizeof(struct CD);    //using this doesn't change things too much... 
#endif 

    for(int j=0;j<numprocs;++j) { 

     total_len += p_lens[j]; 

     if (j==0) { p_disp[j] = 0;          } 
     else  { p_disp[j] = p_disp[j-1] + p_lens[j]*MPI_CD_size; } 
    } 

    if (myid==0) { 

     allData = (struct CD*) calloc((size_t)total_len, sizeof(struct CD));  //allocate array 
     if(!allData) { MPI_Abort(MPI_COMM_WORLD, 1); exit(EXIT_FAILURE); } 
    } 

    MPI_Gatherv(myData, mylen, myMPI_CD, allData, p_lens, p_disp, myMPI_CD, 0, MPI_COMM_WORLD); //each array sends root process their array, which is stored in 'allData' 

    // ============================== OUTPUT CONFIRMING THAT COMMUNICATIONS WERE SUCCESSFUL========================================= 
    if(myid==0) { 

     for(int i=0;i<numprocs;++i) { 
      printf("\n\tElements from %d on MASTER are: { ",i); 
      for(int k=0;k<p_lens[i];++k) { printf("%d: (%d,%lg) ", k, (allData+p_disp[i]+k)->int_ID, (allData+p_disp[i]+k)->dbl_ID); } 

      if(p_lens[i]==0) printf("NOTHING "); 
      printf("}\n"); 
     } 
     printf("\n");  //each data element should appear as two identical numbers, counting upward by the process ID 
    } 
    // ========================================================================================================== 

    if (p_lens) { free(p_lens); p_lens=NULL; }  //adding this in didn't get rid of the MPI_Finalize seg-fault 
    if (p_disp) { free(p_disp); p_disp=NULL; } 
    if (myData) { free(myData); myData=NULL; } 
    if (allData){ free(allData); allData=NULL; }  //the if statement ensures that processes not allocating memory for this pointer don't free anything 

    for(double temp=0.0;temp<1e6;++temp) temp += exp(-10.0); 
    MPI_Barrier(MPI_COMM_WORLD);       //purely for keeping the output organized by give a delay in time 
    printf("ID %d: I have reached the end...before MPI_Type_free!\n", myid); 

    // ====================== CLEAN UP ================================================================================ 
    ERRORCODE = MPI_Type_free(&myMPI_CD);   //this frees the data type...not always necessary, but a good practice 

    for(double temp=0.0;temp<1e6;++temp) temp += exp(-10.0); 
    MPI_Barrier(MPI_COMM_WORLD);        //purely for keeping the output organized by give a delay in time 

    if(ERRORCODE!=MPI_SUCCESS) { printf("ID %d...MPI_Type_free was not successful\n", myid); MPI_Abort(MPI_COMM_WORLD, 911); exit(EXIT_FAILURE); } 
    else      { printf("ID %d...MPI_Type_free was successful, entering MPI_Finalize...\n", myid);  } 
#endif 
    ERRORCODE=MPI_Finalize(); 

    for(double temp=0.0;temp<1e7;++temp) temp += exp(-10.0);  //NO MPI_Barrier AFTER MPI_Finalize! 

    if(ERRORCODE!=MPI_SUCCESS) { printf("ID %d...MPI_Finalize was not successful\n", myid); MPI_Abort(MPI_COMM_WORLD, 911); exit(EXIT_FAILURE); } 
    else      { printf("ID %d...MPI_Finalize was successful\n", myid);  } 

    return EXIT_SUCCESS; 
} 
+1

valgrindメッセージを含めることはできますか?またはvalgrindメッセージの1つまたは2つの代表的なサンプルですか? – sarnold

+0

'call3(3)'呼び出しからキャストを取り除くことをお勧めします。これには ''が含まれていますので、プロトタイプを取得して戻り値をキャストすると警告メッセージを隠すことができます。 – sarnold

+0

@sarnold:あなたが指定したキャストを削除しようとする前に、私はいくつかのvalgrind出力を生成しました。私は詳細をここに投稿しようとしましたが(https://www.mit.edu/~sgildea/)、私が理解できる唯一の明確なメッセージは次のとおりです。 '8行目の無効な読み取り[行Iは収集したデータを印刷しますmalloc'dまたは(最近)free'd'と同じ行番号を参照して、サイズ4の無効な読み込みのための別のメッセージがあります。 – sgildea

答えて

3

K上の外部ループは偽であるが、技術的に間違っありませんそれはちょうど役に立たない。

本当の問題は、MPI_GATHERVへの移動が間違っていることです。

==28749== Invalid write of size 2 
==28749== at 0x4A086F4: memcpy (mc_replace_strmem.c:838) 
==28749== by 0x4C69614: unpack_predefined_data (datatype_unpack.h:41) 
==28749== by 0x4C6B336: ompi_generic_simple_unpack (datatype_unpack.c:418) 
==28749== by 0x4C7288F: ompi_convertor_unpack (convertor.c:314) 
==28749== by 0x8B295C7: mca_pml_ob1_recv_frag_callback_match (pml_ob1_recvfrag.c:216) 
==28749== by 0x935723C: mca_btl_sm_component_progress (btl_sm_component.c:426) 
==28749== by 0x51D4F79: opal_progress (opal_progress.c:207) 
==28749== by 0x8B225CA: opal_condition_wait (condition.h:99) 
==28749== by 0x8B22718: ompi_request_wait_completion (request.h:375) 
==28749== by 0x8B231E1: mca_pml_ob1_recv (pml_ob1_irecv.c:104) 
==28749== by 0x955E7A7: mca_coll_basic_gatherv_intra (coll_basic_gatherv.c:85) 
==28749== by 0x9F7CBFA: mca_coll_sync_gatherv (coll_sync_gatherv.c:46) 
==28749== Address 0x7b1d630 is not stack'd, malloc'd or (recently) free'd 

MPI_GATHERVは何とか悪い情報を与えられたことを示している:あなたはvalgrindのを介して実行した場合、あなたはこのようなものが表示されます。

Open MPI内のlibltdlから来る他のvalgrind警告がありますが残念なことに避けられないものです - libltdlのバグですが、PLPAのバグです。これは意図的にやっているので避けられません。 )]ここで議論することは興味深いtは

あなたの変位の計算を見ると、私は

total_len += p_lens[j];                

    if (j == 0) {                   
     p_disp[j] = 0;                 
    } else {                    
     p_disp[j] = p_disp[j - 1] + p_lens[j] * MPI_CD_size;        
    }                      

を参照してくださいしかし、MPIは、変位がデータ型ではなく、バイト単位である集まります。したがって、実際には次のようになります。

この変更を行うと、MPI_GATHERV valgrindの警告が表示されなくなります。

+0

はい、私はそれが技術的に間違っているわけではないことに同意しますが、実際にOPは何か別のものが起こることを望んでいることを示しています。しかし、良いキャッチ:オフセット+長さの計算は疑わしい。 – wildplasser

+0

@ジェフ:私はあなたの提案を実装しました。なぜ私が遠く離れていたのか - 概念的に - 私が変位を使用しているときに私に正しい出力を与えたのか分かりませんが、この問題を解決してうれしいです。 – sgildea

1

この外側の 'k'ループはちょうど偽です。ボディはk = myid(実行中のプロセスごとに定数)のためだけに実行されます。 kはループ内で決して参照されません(ほぼ一定のmyidとの比較を除いて)。 また、mylen = myid*2;の行が目立ちます。それを定数に変更することをお勧めします。

for (int k=0; k<numprocs; ++k) { 

    if(myid==k) { 

     //printf("\t ID %d has %d entries: { ", myid, mylen); 

     for(int i=0; i<mylen; ++i) { 

      myData[i]= (struct CD) {.int_ID=myid*(i+1), .dbl_ID=myid*(i+1)};   //fills data elements with simple pattern 
      //printf("%d: (%d,%lg) ", i, myData[i].int_ID, myData[i].dbl_ID); 
     } 
     //printf("}\n"); 
    } 
} 

ので、この全体の愚かな構築物はに減少させることができる(MYIDが0とnumprocsの間にあると仮定):

for(int i=0; i<mylen; ++i) { 
     myData[i].int_ID=myid*(i+1); 
     myData[i].dbl_ID=myid*(i+1); 
     } 
+0

私が言ったように、 'k'変数はループ内で決して使用されません。 (myidとの比較は除きますが、これは常に特定のkに当てはまります) – wildplasser

+0

"偽"の意味を理解する助けになりますか?私はそれを配列に入れるための偽のデータを生成する方法として使っています。あなたはそれがMPI_Finalize()のsegfaultに関連していると言っているのですか?それとも私の例が気に入らないのですか?...私はb /私はあなたが最初に参照していたループを誤解していた – sgildea

+0

あなたのプログラムから、あなたの*意図が何であるかを検出することは不可能です。しかし、私はソースから正確に*どのように設定したいのかを知ることはできません。(注:私はmpiに新しいです、私はちょうどCソースを読もうとしています) – wildplasser

関連する問題