2

私は現在、openmpiを使用して並列fftアルゴリズムを実装する必要があるプロジェクトに取り組んでいます。コンパイル中のコードがありますが、クラスター上で実行すると、セグメンテーション違反が発生します。

私は物事がどこでうまくいかないかについての私の勘を持っていますが、効率的な修正を行うことができるようにポインターと参照について十分に理解していないと思います。

うまくいかない可能性のある最初のチャンクは、配列をヘルパー関数に渡すことです。ループに一貫性がないか、これらのポインターを渡して必要なものを取り戻す方法がわからないと思います。

2番目に考えられるスポットは、実際のmpi_Send/Recvコマンド内です。openmpi cデータ型でサポートされていない型を送信しているので、代わりにmpi_byte型を使用して生データを送信しています。これは実行可能なオプションですか?または、この方法の代替案を検討する必要があります。

/* function declarations */
double complex get_block(double complex c[], int start, int stop);

double complex put_block(double complex from[], double complex to[], 
            int start, int stop);

void main(int argc, char **argv)
{
  /* Initialize MPI */
  MPI_Init(&argc, &argv);

  double complex c[N/p];
  int myid;
  MPI_Comm_rank(MPI_COMM_WORLD, &myid);
  //printf("My id is %d\n",myid);

  MPI_Status status;

  int i;
  for(i=0;i<N/p;i++){
    c[i] = 1.0 + 1.0*I;
  }

  int j = log(p)/log(2) + 1;
  double q;
  double complex z;
  double complex w = exp(-2*PI*I/N);
  double complex block[N/(2*p)]; // half the size of chunk c
  int e,l,t,k,m,rank,plus,minus;
  int temp = (log(N)-log(p))/log(2);
  //printf("temp = %d", temp);

  for(e = 0; e < (log(p)/log(2)); e++){
    /* loop constants */
    t = pow(2,e); l = pow(2,e+temp);
    q = n/2*l; z = cpow(w,(complex)q); 
    j = j-1; int v = pow(2,j);

    if(e != 0){
      plus = (myid + p/v)%p;
      minus = (myid - p/v)%p;
    } else {
      plus = myid + p/v;
      minus = myid - p/v;
    }

    if(myid%t == myid%(2*t)){
      MPI_Recv((char*)&c, 
           sizeof(c),
           MPI_BYTE,
           plus,
           MPI_ANY_TAG,
           MPI_COMM_WORLD,
           &status);

      /* transform */
      for(k = 0; k < N/p; k++){
    m = (myid * N/p + k)%l;
    c[k] = c[k] + c[k+N/v] * cpow(z,m);
    c[k+N/v] = c[k] - c[k + N/v] * cpow(z,m); 
    printf("(k,k+N/v) = (%d,%d)\n",k,k+N/v);
    }*/
      printf("\n\n");
      /* end transform */

      *block = get_block(c, N/v, N/v + N/p + 1);
      MPI_Send((char*)&block,
           sizeof(block),
           MPI_BYTE, 
           plus,
           1, 
           MPI_COMM_WORLD);
    } else {
      // send data of this PE to the (i- p/v)th PE
      MPI_Send((char*)&c,
           sizeof(c),
           MPI_BYTE,
           minus,
           1, 
           MPI_COMM_WORLD);
      // after the transformation, receive data from (i-p/v)th PE
      //      and store them in c:
      MPI_Recv((char*)&block,
           sizeof(block),
           MPI_BYTE,
           minus, 
           MPI_ANY_TAG, 
           MPI_COMM_WORLD,
              &status);

      *c = put_block(block, c, N/v, N/v + N/p - 1);
      //printf("Process %d send/receive %d\n",myid, plus);
    }
  }
  /* shut down MPI */
  MPI_Finalize();
}

/* helper functions */
double complex get_block(double complex *c, int start, int stop)
{
  double complex block[stop - start + 1];
  //printf("%d = %d\n",sizeof(block)/sizeof(double complex), sizeof(&c)/sizeof(double  complex));
  int j = 0;
  int i;
  for(i = start; i < stop+1; i++){
    block[j] = c[i];
    j = j+1;
  }
  return *block;
}


double complex put_block(double complex from[], double complex to[], int start, int stop)
{  
  int j = 0;
  int i;
  for(i = start; i<stop+1; i++){
    to[i] = from[j];
    j = j+1;
  }
  return *to;
}

フィードバックに感謝します!

4

2 に答える 2

1

コードをデバッグしてみましたか?これは並列設定では苦痛になる可能性がありますが、失敗している場所と通常はその理由を正確に知ることができます。

LinuxまたはOSXを使用している場合は、コマンドラインで次のようにコードを実行できます。

mpirun -np 4 xterm -e gdb -ex run --args ./yourprog yourargs

ここでyourprog、はプログラムの名前であり、yourargs渡したいコマンドライン引数です。

xtermこのコマンドが行うことは、4つのウィンドウを起動することです。それぞれが、オプションで指定されたxtermとおりに起動します。次に、オプションで指定されたコマンドを実行し、で指定されたオプションを使用して実行可能ファイルを起動します。gdb-egdbrun-ex--args

取得するのはxterm、MPIと並行してプログラムの4つのインスタンスを実行する4つのウィンドウです。インスタンスのいずれかがクラッシュした場合は、gdb場所と理由を教えてくれます。

于 2012-06-19T16:44:53.753 に答える
1

配列/配列へのポインタを間違った方法で使用しています。たとえば、配列をdouble complex block [N]として宣言します。これは問題ありませんが(まれですが、ほとんどの場合、mallocを使用することをお勧めします)、MPI_Recv(&block)を介して配列を受け取ります。ただし、「block」はすでにその配列へのポインターであるため、「&block」と記述することにより、ポインターのポインターをMPI_Recvに渡します。それはそれが期待するものではありません。「&」表記を使用する場合は、&block [0]を記述する必要があります。これにより、ブロック配列の最初の要素へのポインターが得られます。

于 2012-06-19T17:22:48.130 に答える