web-dev-qa-db-ja.com

Cでの構造化シリアライゼーションとMPI

カスタム構造体を定義しており、それを別のMPI MPI_Bsend(またはMPI_Send)を使用するプロセスに送信したい。

これが私の構造です:

struct car{
  int shifts;
  int topSpeed;
}myCar;

ただし、プリミティブ型を除いてMPIは、上記の構造体のような複雑なデータ型の直接の「送信」をサポートしていないようです。「シリアル化」を使用する必要があるかもしれないと聞きました。私はそれについて行き、「myCar」をプロセス5に送信しますか?

30
kstratis

ジェレミアは正しいです-MPI_Type_create_structはここに行く方法です。

MPIはライブラリであり、言語に組み込まれていないため、それ自体がシリアル化するために構造がどのように見えるかを「見る」ことはできません。したがって、複雑なデータ型を送信するには、 、そのレイアウトを明示的に定義する必要があります。シリアライゼーションをネイティブでサポートしている言語では、MPIラッパーのセットがそれを確実に利用できます; たとえば、mpi4py はpythonの pickle を利用して複雑なデータ型を透過的に送信しますが、Cではロールする必要があります袖を上にして自分でやります。

あなたの構造では、これは次のようになります:

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#include <stddef.h>

typedef struct car_s {
        int shifts;
        int topSpeed;
} car;

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

    const int tag = 13;
    int size, rank;

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

    if (size < 2) {
        fprintf(stderr,"Requires at least two processes.\n");
        exit(-1);
    }

    /* create a type for struct car */
    const int nitems=2;
    int          blocklengths[2] = {1,1};
    MPI_Datatype types[2] = {MPI_INT, MPI_INT};
    MPI_Datatype mpi_car_type;
    MPI_Aint     offsets[2];

    offsets[0] = offsetof(car, shifts);
    offsets[1] = offsetof(car, topSpeed);

    MPI_Type_create_struct(nitems, blocklengths, offsets, types, &mpi_car_type);
    MPI_Type_commit(&mpi_car_type);

    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    if (rank == 0) {
        car send;
        send.shifts = 4;
        send.topSpeed = 100;

        const int dest = 1;
        MPI_Send(&send,   1, mpi_car_type, dest, tag, MPI_COMM_WORLD);

        printf("Rank %d: sent structure car\n", rank);
    }
    if (rank == 1) {
        MPI_Status status;
        const int src=0;

        car recv;

        MPI_Recv(&recv,   1, mpi_car_type, src, tag, MPI_COMM_WORLD, &status);
        printf("Rank %d: Received: shifts = %d topSpeed = %d\n", rank,
                 recv.shifts, recv.topSpeed);
    }

    MPI_Type_free(&mpi_car_type);
    MPI_Finalize();

    return 0;
}
58
Jonathan Dursi

Jonathan Dursiの答えは正しいですが、非常に複雑です。 MPIは、問題により適した、より単純で一般的でない型コンストラクタを提供します。MPI_Type_create_structは、異なる基本型(intとfloatなど)がある場合にのみ必要です。

あなたの例では、いくつかのより良い解決策があります:

  • 2つの整数が連続したメモリ領域(つまり、整数の配列のように)に配置されていると仮定すると、派生データ型はまったく必要ありません。送受信バッファとして使用されるcar型の変数のアドレスを使用して、MPI_INT型の2つの要素を送受信するだけです。

    MPI_Send(&send, 2, MPI_INT, dest, tag, MPI_COMM_WORLD);
    MPI_Recv(&recv, 2, MPI_INT, src, tag, MPI_COMM_WORLD, &status);
    
  • 派生データ型を使用したい場合(たとえば、読みやすさやその面白さのため)、配列に対応するMPI_Type_contiguousを使用できます。

    MPI_Type_contiguous(2, MPI_INT, &mpi_car_type);
    
  • 2つの整数の配置が異なる場合(ほとんどの場合はそうではありませんが、マシンに依存し、MPI実装は多くの異なるプラットフォームに存在します)、MPI_Type_indexed_blockを使用できます。変位の配列(MPI_Type_create_structなど)を取りますが、oldtype引数は1つだけで、各ブロックのブロック長は定義により1です。

    MPI_Aint offsets[2];
    offsets[0] = offsetof(car, shifts) ; //most likely going to be 0 
    offsets[1] = offsetof(car, topSpeed);
    MPI_Type_indexed_block(2, offsets, MPI_INT);
    

他のソリューションは意味的には正しいですが、読むのがはるかに難しく、パフォーマンスが大幅に低下する可能性があります。

12
mort

見る MPI_Type_create_structを使用して、オブジェクトのカスタムMPI=データ型を作成します。これを使用する例は、 http://beige.ucs.indiana.edu/I590にあります。 /node100.html

6
_int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
_

OpenMPIは、bufで始まるcount * sizeof(datatype)の連続バイトを送信して、int配列などを送信できるようにします。たとえば、10 int配列_int arr[10]_を宣言した場合、次のように送信できます

_MPI_Send(arr, 10, MPI_INT, 1, 0, MPI_COMM_WORLD);
_

同様に受け取ります。 bufはvoidポインターであるため、これを悪用してsizeof(my_struct)バイトを送信し、受信側で構造体としてキャストすることにより、構造体を送信できます。次に例を示します。

_#include "mpi.h"
#include <stdio.h>

typedef struct 
{
    char a;
    int b;
    short c;
} my_struct;


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

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &taskid);
    MPI_Comm_size(MPI_COMM_WORLD, &numtasks);


    if (taskid == 0) 
    {
        my_struct m;
        m.a = '!';
        m.b = 1234;
        m.c = 5678;

        MPI_Send(&m, sizeof(my_struct), MPI_CHAR, 1, 0, MPI_COMM_WORLD);
    }
    else 
    {
        my_struct m;
        MPI_Recv(&m, sizeof(my_struct), MPI_CHAR, 0, 0, MPI_COMM_WORLD, 
                 MPI_STATUS_IGNORE);
        printf("%c %d %d\n", m.a, m.b, m.c); 
    }

    MPI_Finalize();
}
_

C配列は連続してデータを格納するため、構造体の配列を送信することもできます 構造体の配列を割り当てる方法 。したがって、_my_struct m_array[10]_がある場合は、次のように送信(および同様に受信)します。

_MPI_Send(m_array, sizeof(my_struct) * 10, MPI_CHAR, 1, 0, MPI_COMM_WORLD);
_
0
qwr