2

しばらくの間、コードにバグがあり、それを解決する方法がまだわかりませんでした。

私が達成しようとしていることは簡単です: すべてのワーカーノード (つまり、ランク!=0 のノード) は、計算を伴う正方形構造で行 (1 次元の arry で表される) を取得します。計算が完了すると、この行がマスターに送り返されます。

テスト目的のため、計算は含まれていません。起こっていることはすべて次のとおりです。

  • マスターはワーカーに行番号を送信し、ワーカーは行番号を使用してそれに応じた値を計算します
  • ワーカーは結果値を含む配列を送り返します

今、私の問題はこれです:

  • all は、行内の要素数 (サイズ = 1006) およびワーカー数 > 1 の特定のサイズまで期待どおりに機能します。
  • 行の要素が 1006 を超えると、ワーカーはシャットダウンに失敗し、プログラムは終了しません。
  • これは、配列をマスターに送り返そうとした場合にのみ発生します。単純に INT を送り返すだけで問題ありません (doMasterTasks() と doWorkerTasks() のコメントアウト行を参照)。

最後の箇条書きに基づいて、マスターに送り返される配列が特定のサイズに達した場合にのみ表面化する何らかの競合状態が存在するに違いないと思います。

問題が何であるか考えていますか?

次のコードをコンパイルします: mpicc -O2 -std=c99 -o simple

次のように実行可能ファイルを実行します: mpirun -np 3 simple <size> (例: 1006 または 1007)

コードは次のとおりです。

#include "mpi.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MASTER_RANK 0
#define TAG_RESULT 1
#define TAG_ROW 2
#define TAG_FINISHOFF 3

int mpi_call_result, my_rank, dimension, np;

// forward declarations
void doInitWork(int argc, char **argv);
void doMasterTasks(int argc, char **argv);
void doWorkerTasks(void);
void finalize();
void quit(const char *msg, int mpi_call_result);

void shutdownWorkers() {
    printf("All work has been done, shutting down clients now.\n");
    for (int i = 0; i < np; i++) {
        MPI_Send(0, 0, MPI_INT, i, TAG_FINISHOFF, MPI_COMM_WORLD);
    }
}

void doMasterTasks(int argc, char **argv) {
    printf("Starting to distribute work...\n");
    int size = dimension;
    int * dataBuffer = (int *) malloc(sizeof(int) * size);

    int currentRow = 0;
    int receivedRow = -1;
    int rowsLeft = dimension;
    MPI_Status status;

    for (int i = 1; i < np; i++) {
        MPI_Send(&currentRow, 1, MPI_INT, i, TAG_ROW, MPI_COMM_WORLD);
        rowsLeft--;
        currentRow++;

    }

    for (;;) {
//        MPI_Recv(dataBuffer, size, MPI_INT, MPI_ANY_SOURCE, TAG_RESULT, MPI_COMM_WORLD, &status);
        MPI_Recv(&receivedRow, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);

        if (rowsLeft == 0)
            break;

        if (currentRow > 1004)
            printf("Sending row %d to worker %d\n", currentRow, status.MPI_SOURCE);
        MPI_Send(&currentRow, 1, MPI_INT, status.MPI_SOURCE, TAG_ROW, MPI_COMM_WORLD);
        rowsLeft--;
        currentRow++;
    }
    shutdownWorkers();
    free(dataBuffer);
}

void doWorkerTasks() {
    printf("Worker %d started\n", my_rank);

    // send the processed row back as the first element in the colours array.
    int size = dimension;
    int * data = (int *) malloc(sizeof(int) * size);
    memset(data, 0, sizeof(size));

    int processingRow = -1;
    MPI_Status status;

    for (;;) {

        MPI_Recv(&processingRow, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
        if (status.MPI_TAG == TAG_FINISHOFF) {
            printf("Finish-OFF tag received!\n");
            break;
        } else {
//            MPI_Send(data, size, MPI_INT, 0, TAG_RESULT, MPI_COMM_WORLD);
            MPI_Send(&processingRow, 1, MPI_INT, 0, TAG_RESULT, MPI_COMM_WORLD);
        }
    }

    printf("Slave %d finished work\n", my_rank);
    free(data);
}

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


    if (argc == 2) {
        sscanf(argv[1], "%d", &dimension);
    } else {
        dimension = 1000;
    }

    doInitWork(argc, argv);

    if (my_rank == MASTER_RANK) {
        doMasterTasks(argc, argv);
    } else {
        doWorkerTasks();
    }
    finalize();
}

void quit(const char *msg, int mpi_call_result) {
    printf("\n%s\n", msg);
    MPI_Abort(MPI_COMM_WORLD, mpi_call_result);
    exit(mpi_call_result);
}

void finalize() {
    mpi_call_result = MPI_Finalize();
    if (mpi_call_result != 0) {
        quit("Finalizing the MPI system failed, aborting now...", mpi_call_result);
    }
}

void doInitWork(int argc, char **argv) {
    mpi_call_result = MPI_Init(&argc, &argv);
    if (mpi_call_result != 0) {
        quit("Error while initializing the system. Aborting now...\n", mpi_call_result);
    }
    MPI_Comm_size(MPI_COMM_WORLD, &np);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
}

どんな助けでも大歓迎です!

ベスト、クリス

4

1 に答える 1

6

doWorkerTasks を見ると、受信したデータ メッセージとまったく同じ数のデータ メッセージが送信されていることがわかります。(そして、シャットダウンするためにもう1つ受け取ります)。

しかし、あなたのマスターコード:

for (int i = 1; i < np; i++) {
    MPI_Send(&currentRow, 1, MPI_INT, i, TAG_ROW, MPI_COMM_WORLD);
    rowsLeft--;
    currentRow++;

}

for (;;) {
    MPI_Recv(dataBuffer, size, MPI_INT, MPI_ANY_SOURCE, TAG_RESULT, MPI_COMM_WORLD, &status);

    if (rowsLeft == 0)
        break;

    MPI_Send(&currentRow, 1, MPI_INT, status.MPI_SOURCE, TAG_ROW, MPI_COMM_WORLD);
    rowsLeft--;
    currentRow++;
}

受信するよりも np-2 多いデータ メッセージを送信します。特に、未処理のデータ メッセージが np-2 あるはずなのに、送信するデータがなくなるまでデータを受信し続けます。コードを次のように変更します。

int rowsLeftToSend= dimension;
int rowsLeftToReceive = dimension;

for (int i = 1; i < np; i++) {
    MPI_Send(&currentRow, 1, MPI_INT, i, TAG_ROW, MPI_COMM_WORLD);
    rowsLeftToSend--;
    currentRow++;

}

while (rowsLeftToReceive > 0) {
    MPI_Recv(dataBuffer, size, MPI_INT, MPI_ANY_SOURCE, TAG_RESULT, MPI_COMM_WORLD, &status);
    rowsLeftToReceive--;

    if (rowsLeftToSend> 0) {
        if (currentRow > 1004)
            printf("Sending row %d to worker %d\n", currentRow, status.MPI_SOURCE);
        MPI_Send(&currentRow, 1, MPI_INT, status.MPI_SOURCE, TAG_ROW, MPI_COMM_WORLD);
        rowsLeftToSend--;
        currentRow++;
    }
}

今すぐ動作します。

メッセージサイズが小さい場合にコードがデッドロックしない理由 (これは競合状態ではなくデッドロックであることに注意してください。これは分散コンピューティングでより一般的な並列エラーです) は、ほとんどの MPI 実装がどのように機能するかの微妙な詳細です。一般に、MPI 実装は、受信側の準備ができているかどうかに関係なく、小さなメッセージをパイプに「押し込む」だけですが、大きなメッセージ (受信側でより多くのストレージ リソースを必要とするため) は、送信側と受信側の間で何らかのハンドシェイクが必要になります。(さらに詳しく知りたい場合は、熱心な対ランデブー プロトコルを検索してください)。

したがって、小さなメッセージの場合 (この場合は 1006 未満の int であり、1 int も確実に機能します)、マスターがメッセージを受信して​​いるかどうかにかかわらず、ワーカー ノードは送信を行いました。マスターMPI_Recv() を呼び出した場合、メッセージは既にそこにあり、すぐに返されます。しかしそうではなかったので、マスター側に保留中のメッセージがありました。しかし、それは問題ではありませんでした。マスターがキル メッセージを送信し、全員が終了しました。

しかし、より大きなメッセージの場合、残りの send() では受信者がクリアに参加する必要があり、受信者が関与しないため、残りのワーカーはハングします。

デッドロックが発生しなかった小さなメッセージの場合でも、コードが正しく機能しなかったことに注意してください。計算されたデータが欠落していました。

更新:あなたに同様の問題がありましたshutdownWorkers:

void shutdownWorkers() {
    printf("All work has been done, shutting down clients now.\n");
    for (int i = 0; i < np; i++) {
        MPI_Send(0, 0, MPI_INT, i, TAG_FINISHOFF, MPI_COMM_WORLD);
    }
}

ここでは、送信を行っているランク 0を含むすべてのプロセスに送信しています。原則として、MPI_Send はブロック送信であり、一致する受信がまだポストされていないため、デッドロックする必要があります。これを回避するために、事前にノンブロッキング受信を投稿できますが、それは不要です。ランク 0 は、終了を知らせる必要はありません。したがって、ループを次のように変更するだけです

    for (int i = 1; i < np; i++)

tl;dr - マスターがワーカーから十分なメッセージを受信して​​いないため、コードがデッドロックしました。ほとんどの MPI ライブラリに共通の実装の詳細により、小さなメッセージ サイズでも機能することがありました。

于 2011-10-22T14:58:12.593 に答える