1

配列を格納する配列(sendbuffと呼ばれる)があり、MPI::Scatterを使用してそれらの配列を他のスレッドに送信したいと思います。

                        sendbuff
   #####    ###############################
p  # 0 # -> # -1 # -1 # -1 # -1 # -1 # -1 # (values)
o  #####    ###############################
s  # 1 # -> # -1 # -1 # -1 # -1 # -1 # -1 # (values)
   #####    ###############################

ご覧のとおり、6つの値(すべて-1)を持ち、同じものsendbuff[0]を持つサイズ6の配列を保持します。sendbuff[1]-1の配列を他のスレッドに送信し、0で埋められたrecvbuffという配列に保存したいと思います。

        recvbuff
#########################
# 0 # 0 # 0 # 0 # 0 # 0 #
#########################

OSで答えを調べたところ、いくつか見つかりましたが、MPI_Datatypeを使用していますが、避けたいと思います。この目標を達成するために、機能しない次のコードを作成しました。

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

    //variable innitialization
    int taskid, ntasks, buffsize, **sendbuff, *recvbuff;

    MPI::Init(argc, argv);

    taskid = MPI::COMM_WORLD.Get_rank();
    ntasks = MPI::COMM_WORLD.Get_size();    

    buffsize = 6;

    //memory innitialization
    recvbuff = new int[buffsize];
    sendbuff = new int*[ntasks];
    for(int i = 0; i < ntasks; i++){
        sendbuff[i] = new int[buffsize];
    }

    //array innitialization
    for(int i = 0; i < buffsize; i++){
        recvbuff[i] = 0;
    }
    for(int i = 0; i < ntasks; i++){
        for(int j = 0; j < buffsize; j++){
            sendbuff[i][j] = -1;
        }
    }

    //communication
    MPI::COMM_WORLD.Scatter(sendbuff[0], buffsize, MPI::INT, recvbuff, buffsize,
        MPI::INT, 0);

    //output
    for(int i = 0; i < buffsize; i++){
        cout<<"Task"<<taskid<<" recvbuff["<<i<<"] = "<<recvbuff[i] << endl;
    }

    //cleaning
    for(int i = 0; i < ntasks; i++){
            delete[] sendbuff[i];
    }
    delete[] sendbuff;
    delete[] recvbuff;


    MPI::Finalize();

    return EXIT_SUCCESS;
}

スキャッターを使用した後、彼のrecvbuff変数は-1の値で満たされると思いますが、次のように-1とガベージが混在しています。

$ mpirun -np 3 a.out 
Task0 recvbuff[0] = -1
Task0 recvbuff[1] = -1
Task0 recvbuff[2] = -1
Task0 recvbuff[3] = -1
Task0 recvbuff[4] = -1
Task0 recvbuff[5] = -1    
Task1 recvbuff[0] = 33
Task1 recvbuff[1] = 0
Task1 recvbuff[2] = -1
Task1 recvbuff[3] = -1
Task1 recvbuff[4] = -1
Task1 recvbuff[5] = -1
Task2 recvbuff[0] = -1
Task2 recvbuff[1] = -1
Task2 recvbuff[2] = 33
Task2 recvbuff[3] = 0
Task2 recvbuff[4] = 1768975727
Task2 recvbuff[5] = 7496543

私が間違っていることは何ですか?よろしくお願いします、ペドロ。

4

1 に答える 1

2

スキャッターとギャザーは、この回答で詳細に説明されています。Scatterはデータを分割し、断片を他のタスクに分散しますが、データは連続したメモリに保存する必要があります-MPI_Scatterは、ポインターをたどる必要があること、もしそうなら、いくつあるか、そしてsendbuffを割り当てる方法を知る方法がありません:

sendbuff = new int*[ntasks];
for(int i = 0; i < ntasks; i++){
    sendbuff[i] = new int[buffsize];
}

sendbuffのさまざまな行が、システムメモリ全体に散在している可能性があります。データを連続して割り当てると、ほぼそこにいます。

sendbuff = new int*[ntasks];
sendbuff[0] = new int[ntasks * 6];
for(int i = 1; i < ntasks; i++){
    sendbuff[i] = &(sendbuff[0][i*6];
}

これで分散できるはずですが、行0がランク0になることに注意してください。つまり、スキャッターはコミュニケーター内のすべてのプロセスに行きます。ランク0以外のタスクに送信しようとしているだけの場合、最も簡単な方法は、通常のスキャッターが正しく機能するように、ランク0のsendbuffにダミーデータの行を保持することです。

#include <iostream>
#include <mpi.h>

int main(int argc, char **argv)
{
    int rank, size;
    const int nelem = 6;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    int **sendbuff = new int*[size];
    int *recvbuff  = new int[nelem];

    if (rank == 0) {
        sendbuff[0] = new int[nelem * size];
        for (int i=0; i<size; i++)
            sendbuff[i] = &(sendbuff[0][nelem*i]);

        for (int i=0; i<size; i++)
            for (int j=0; j<nelem; j++)
                sendbuff[i][j] = i-1;
    }

    MPI_Scatter(sendbuff[0], nelem, MPI_INT, recvbuff, nelem, MPI_INT, 0, MPI_COMM_WORLD);

    if (rank != 0) {
        std::cout << "Scatter: [ " << rank << "]: ";
        for (int i=0; i<nelem; i++)
            std::cout << recvbuff[i] << " ";
        std::cout << std::endl;

        for (int i=0; i<nelem; i++)
            recvbuff[i] *= recvbuff[i];
    }

    MPI_Gather(recvbuff, nelem, MPI_INT, sendbuff[0], nelem, MPI_INT, 0, MPI_COMM_WORLD);
    if (rank == 0) {
        for (int j=1; j<size; j++) {
            std::cout << "Gather: [ " << j << "]: ";
            for (int i=0; i<nelem; i++)
                    std::cout << sendbuff[j][i] << " ";
            std::cout << std::endl;
        }
    }

    delete [] recvbuff;
    if (rank == 0)
        delete [] sendbuff[0];
    delete [] sendbuff;

    MPI_Finalize();
}

データを分散させ、ワーカーが数値を2乗し、マスターがデータを収集していることに注意してください。コンパイルして実行すると、次のようになります。

$ mpic++ -o intercomm intercomm.cxx
$ mpirun -np 4 ./intercomm
Scatter: [ 2]: 1 1 1 1 1 1
Scatter: [ 1]: 0 0 0 0 0 0
Scatter: [ 3]: 2 2 2 2 2 2
Gather: [ 1]: 0 0 0 0 0 0
Gather: [ 2]: 1 1 1 1 1 1
Gather: [ 3]: 4 4 4 4 4 4

ランク0のダミーデータ(おそらく大きい)を避けたい場合は、タスクをマスタータスクとワーカータスクの2つのグループに分割し、それらの間の集合的な通信を可能にする相互通信機能を設定できます。これはまさにそれを行う簡単なプログラムです:

#include <iostream>
#include <mpi.h>

int main(int argc, char **argv)
{
    MPI_Comm   localComm;    /* intra-communicator of local sub-group */
    MPI_Comm   interComm;    /* inter-communicator */
    int masterworker;
    int rank, size;
    const int nelem = 6;
    int rootrank;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    masterworker = (rank == 0 ? 0 : 1);
    MPI_Comm_split(MPI_COMM_WORLD, masterworker, rank, &localComm);

    if (masterworker == 0)
    {
        MPI_Intercomm_create( localComm, 0, MPI_COMM_WORLD, 1, 1, &interComm);
        rootrank = ( rank == 0 ? MPI_ROOT : MPI_PROC_NULL );
    }
    else {
        MPI_Intercomm_create( localComm, 0, MPI_COMM_WORLD, 0, 1, &interComm);
        rootrank = 0;
    }

    int **sendbuff = new int*[size-1];
    int *recvbuff  = new int[nelem];

    if (rank == 0) {

        sendbuff[0] = new int[nelem * (size-1)];
        for (int i=1; i<size-1; i++)
            sendbuff[i] = &(sendbuff[0][nelem*i]);

        for (int i=0; i<size-1; i++)
            for (int j=0; j<nelem; j++)
                sendbuff[i][j] = i;
    }

    MPI_Scatter(sendbuff[0], nelem, MPI_INT, recvbuff, nelem, MPI_INT, rootrank, interComm);

    if (masterworker == 1) {
        std::cout << "Scatter: [ " << rank << "]: ";
        for (int i=0; i<nelem; i++)
            std::cout << recvbuff[i] << " ";
        std::cout << std::endl;

        for (int i=0; i<nelem; i++)
            recvbuff[i] *= recvbuff[i];
    }

    MPI_Gather(recvbuff, nelem, MPI_INT, sendbuff[0], nelem, MPI_INT, rootrank, interComm);
    if (masterworker == 0) {
        for (int j=0; j<size-1; j++) {
            std::cout << "Gather: [ " << j << "]: ";
            for (int i=0; i<nelem; i++)
                    std::cout << sendbuff[j][i] << " ";
            std::cout << std::endl;
        }
    }




    MPI_Comm_free(&interComm);
    MPI_Comm_free(&localComm);
    delete [] recvbuff;
    if (rank == 0)
        delete [] sendbuff[0];
    delete [] sendbuff;

    MPI_Finalize();
}

繰り返しますが、コンパイルして実行すると、次のようになります。

$ mpic++ -o intercomm intercomm.cxx
$ mpirun -np 4 ./intercomm
Scatter: [ 1]: 0 0 0 0 0 0
Scatter: [ 2]: 1 1 1 1 1 1
Scatter: [ 3]: 2 2 2 2 2 2
Gather: [ 0]: 0 0 0 0 0 0
Gather: [ 1]: 1 1 1 1 1 1
Gather: [ 2]: 4 4 4 4 4 4

または、インターコミュニケーターをいじりたくない場合は、通常のスキャッターが正しく機能するように、ランク0のsendbuffにダミーデータの行を保持するだけです。

#include <iostream>
#include <mpi.h>

int main(int argc, char **argv)
{
    int rank, size;
    const int nelem = 6;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    int **sendbuff = new int*[size];
    int *recvbuff  = new int[nelem];

    if (rank == 0) {
        sendbuff[0] = new int[nelem * size];
        for (int i=0; i<size; i++)
            sendbuff[i] = &(sendbuff[0][nelem*i]);

        for (int i=0; i<size; i++)
            for (int j=0; j<nelem; j++)
                sendbuff[i][j] = i-1;
    }

    MPI_Scatter(sendbuff[0], nelem, MPI_INT, recvbuff, nelem, MPI_INT, 0, MPI_COMM_WORLD);

    if (rank != 0) {
        std::cout << "Scatter: [ " << rank << "]: ";
        for (int i=0; i<nelem; i++)
            std::cout << recvbuff[i] << " ";
        std::cout << std::endl;

        for (int i=0; i<nelem; i++)
            recvbuff[i] *= recvbuff[i];
    }

    MPI_Gather(recvbuff, nelem, MPI_INT, sendbuff[0], nelem, MPI_INT, 0, MPI_COMM_WORLD);
    if (rank == 0) {
        for (int j=1; j<size; j++) {
            std::cout << "Gather: [ " << j << "]: ";
            for (int i=0; i<nelem; i++)
                    std::cout << sendbuff[j][i] << " ";
            std::cout << std::endl;
        }
    }

    delete [] recvbuff;
    if (rank == 0)
        delete [] sendbuff[0];
    delete [] sendbuff;

    MPI_Finalize();
}

また、コンパイルして実行すると、次のようになります。

$ mpic++ -o intercomm intercomm.cxx
$ mpirun -np 4 ./intercomm
Scatter: [ 2]: 1 1 1 1 1 1
Scatter: [ 1]: 0 0 0 0 0 0
Scatter: [ 3]: 2 2 2 2 2 2
Gather: [ 1]: 0 0 0 0 0 0
Gather: [ 2]: 1 1 1 1 1 1
Gather: [ 3]: 4 4 4 4 4 4
于 2013-03-23T16:26:18.167 に答える