11

MPI (C) に関連する 3 つの質問があります。最初の 2 つは同じ答えだと思いますが、肯定的ではありません。


質問1

を使用MPI_Dims_createして 2D グリッドを作成する場合、返されるディメンションのうち X と Y はどれですか?

例えば:

int numProcs = -1, myProcID = -1;
MPI_Comm_rank(MPI_COMM_WORLD, &myProcID);
MPI_Comm_size(MPI_COMM_WORLD, &numProcs);

int size[2];
size[0] = size[1] = 0;
MPI_Dims_create(numProcs, 2, size);
int numProcs_y = -1, numProcs_x = 1;

それは次のとおりです。

numProcs_y = size[0];
numProcs_x = size[1];

またはこれ:

numProcs_x = size[0];
numProcs_y = size[1];

私はこれをいくつかのプロセスで実行してみましたが、答えが得られると思われました (例: 6)。6 つのプロセスでMPI_Dims_createは、3 行 2 列または 2 行 3 列のグリッドを作成する必要があります (ドキュメントを誤解していない限り)。プロセス 3 (6 のうち) について、size[0] = 1およびsize[1] = 0(これは x = 0、y = 1 に対応すると思います) であることがわかりました。これはMPI_Dims_create、3 行 2 列のグリッドを作成していることを示しているようです (2 行 3 列であるため、プロセス 2 (6 個中) は x = 2、y = 0 である必要があります)。誰かがこれについて提供できる確認は大歓迎です。


質問2

2D デカルト グリッドで使用MPI_Cart_coordsする場合、返されるディメンションのうち X と Y はどれですか?

例えば:

int periodic[2];
periodic[0] = periodic[1] = 0; // no wrap around
MPI_Comm cart_comm;
int coords[2];

// using size from question 1
MPI_Cart_create(MPI_COMM_WORLD, 2, size, periodic, 1, &cart_comm);
MPI_Cart_coords(cart_comm, myProcID, 2, coords);

質問 1 と同様に、私の質問は、このようにすべきかということです

myProcID_y = coords[0];
myProcID_x = coords[1];

またはこのように

myProcID_x = coords[0];
myProcID_y = coords[1];

ここでドキュメントと以前の質問を検索してきましたが、この質問に対する直接的な答えが見つからないようです。

ドキュメントは、2 つのアプローチのうち最初のアプローチが正しいことを示しているようですが、明確には述べていません。


質問 3

最初の 2 つの質問の背後にある根本的な質問は、2D グリッドを行と列に分割しようとしているということです。ただし、MPI_Comm_rankその後、各プロセスの行と列の ID を確認するために使用すると、上記の質問に対する答えとは一致しない順序でプロセスが並べられています。

上記に基づいて、プロセスは次のように並べられると思います。

P0 P1
P2 P3
P4 P5

ただし、このコードを使用すると (これは私のプログラムの上記のコードの後に​​あるため、上記のすべてのコードにアクセスできます。質問を簡単に分離できるように、コードを分離しようとしています):

MPI_Comm row_comm, col_comm;
int row_id = -1, col_id = -1;
// ** NOTE: My use of myProcID_y and myProcID_x here are based 
// on my understanding of the previous 2 questions ... if my 
// understanding to one/both of those is wrong, then obviously the assignments 
// here are wrong too.
// create row and column communicators based on my location in grid
MPI_Comm_split(cart_comm, myProcID_y, myProcID_x, &row_comm);
MPI_Comm_split(cart_comm, myProcID_x, myProcID_y, &col_comm);

// get row and column ID for each process
MPI_Comm_rank(row_comm, &row_id);
MPI_Comm_rank(col_comm, &col_id);
printf("Process: %d\trowID: %d\tcolID: %d\n", myProcID, row_id, col_id);

次の印刷物が表示されます。

Process: 0 rowID: 0        colID: 0
Process: 1 rowID: 1        colID: 0
Process: 2 rowID: 0        colID: 1
Process: 3 rowID: 1        colID: 1
Process: 4 rowID: 0        colID: 2
Process: 5 rowID: 1        colID: 2

これは、次のプロセスの順序に対応しているようです。

P0 P2 P4
P1 P3 P5

これは、私が期待していたものとは反対の行列次元 (2 行、3 列)MPI_Dims_createです。

最初の 2 つの質問に対する私の理解が正しい (つまり、Y がこれらの関数によって返される最初の次元である) と仮定すると、このステップでプロセスが (一見) 異なる順序で並べられているのはなぜですか?

4

1 に答える 1

6

Q1 と Q2。MPI には次元 X や次元 Y などはありません。これらは、標準で使用される抽象的な次元に付けるラベルです。MPI は番号付けされた次元で動作し、ランクの C 行優先の番号付けに従います。つまり、2x3デカルト トポロジーで(0,0)は、rank0(0,1)マップし、rank にマップし、rank1(0,2)マップし、rank2(1,0)マップし3ます。

ディメンションは、座標タプルの右端0の要素に対応することに注意してください。これは、 C 配列の要素の番号付けとはであり、しばしば混乱の原因となります。デカルト トポロジを作成するには、配列を次のように初期化する必要があります。2x3size

int size[2] = { 3, 2 };

抽象的な番号付きの次元を問題にマッピングするのはあなた次第です。ディメンション0を X にするか、ディメンションを選択するか1は問題ではありません。

に関してはMPI_DIMS_CREATE、標準は次のように述べています。

次元は、適切な分割アルゴリズムを使用して、互いにできるだけ近くなるように設定されます。

dims[i]呼び出しによるセットの為dims[i]、昇順でない順となります。

この操作は、次のプロパティを持つ要素の配列を返すだけですdims[i](ただし、 をdims[]呼び出す前に にゼロ以外の値を設定して、1 つ以上の次元のサイズを固定している場合を除きますMPI_DIMS_CREATE)。

  • dims[0] >= dims[1] >= dims[2] >= ...
  • dims[0] * dims[1] * dims[2] == nprocs、ここで、nprocsは に指定されたプロセスの数ですMPI_DIMS_CREATE

これは、 がMPI_DIMS_CREATEのセットをnprocs多次元グリッドに分解するときに、最大の乗数を dimension のサイズに割り当て、0次の乗数を dimension のサイズに割り当て、というように割り当て1ます。6のような要因として2*3MPI_DIMS_CREATEが返され{ 3, 2 }ます。MPI_CART_CREATEからの結果で直接呼び出すと、座標とランクをMPI_DIMS_CREATE持つトポロジが作成されます。2x3

(0,0)=0 (0,1)=1 (0,2)=2
(1,0)=3 (1,1)=4 (1,2)=5

Q3. MPI はデカルト トポロジーを分割するための特別なルーチンを提供します - MPI_CART_SUB. remain_dims標準で命名された論理フラグ (C では整数) の配列を取ります。0 以外の各remain_dims[i]値は、そのディメンションiが結果のパーティション分割で保持される必要があることを意味しますが、保持されないディメンションの可能な組み合わせに対して個別のサブコミュニケーターが作成されます。たとえば、次の2x3トポロジがあるとします。

  • remain_dims[] = { 1, 0 }次元を保持し0、それぞれ 3 つのプロセスを持つ 2 つのオーバーラップしない 1-d コミュニケーターになります。
  • remain_dims[] = { 0, 1 }ディメンションを保持する1と、それぞれ 2 つのプロセスを持つ 3 つの重複しない 1-d コミュニケーターが生成されます。
  • remain_dims[] = { 0, 0 }2 つの次元のいずれも保持されず、それぞれに 1 つのプロセスを持つ 6 つの重複しないゼロ次元コミュニケーターが生成されます。

行方向のパーティション分割と列方向のパーティション分割のどちらを呼び出すかは、デカルト次元のラベル付け次第です。


注意すべきことの 1 つは、ネットワーク化された SMP または NUMA マルチコア ノードで構成されるシステムで MPI コードを実行することが多いということです。この場合、適切な 2D グリッドは になりますnodes x cores。コアの数がわかっている場合は、次の呼び出しで簡単に修正できますMPI_DIMS_CREATE

int size[2] = { ncores, 0 };

MPI_Dims_create(numProcs, 2, size);

numProcsこれは、 で除算して割り切れないかどうかncoresをチェックするよりも便利です。次に、次元を保持するパーティショニング、つまりMPI_DIMS_CREATEncoresnumProcs0

int remain_dims[2] = { 1, 0 };

同じノード上のプロセスを含むサブコミュニケーターを作成しますが、

int remain_dims[2] = { 0, 1 };

同じノードからの 2 つのプロセスを含まないサブコミュニケーターを作成します。


1 (true)コードで、 のreorderパラメータにの値を指定したことに注意してくださいMPI_CART_CREATEMPI_COMM_WORLDこれにより、デカルト コミュニケーター内と内でプロセスのランクが異なる可能性があります。したがって、次のコード行が期待どおりに動作するという保証はありません。

MPI_Cart_coords(cart_comm, myProcID, 2, coords);
                           ^^^^^^^^
...
printf("Process: %d\trowID: %d\tcolID: %d\n", myProcID, row_id, col_id);
                                              ^^^^^^^^

myProcIDから取得されMPI_COMM_WORLD、実際には の新しいランクと異なる可能性があるcart_commため、プロセス座標の取得や分割の実行には使用しないでください。

于 2012-12-09T00:13:50.090 に答える