web-dev-qa-db-fra.com

struct sérialisation en C et transfert sur MPI

J'ai défini une structure personnalisée et je souhaite l'envoyer à un autre MPI processus utilisant le MPI_Bsend (ou MPI_Send).

Voici ma structure:

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

Cependant, en dehors des types primitifs MPI ne semble pas prendre en charge la "transmission" directe de types de données complexes comme la structure ci-dessus. J'ai entendu dire que je pourrais avoir à utiliser la "sérialisation". Comment J'y vais et j'envoie 'myCar' pour traiter 5?

30
kstratis

Jérémie a raison - MPI_Type_create_struct est le chemin à parcourir ici.

Il est important de se rappeler que MPI est une bibliothèque, non intégrée dans le langage; elle ne peut donc pas "voir" à quoi ressemble une structure pour la sérialiser par elle-même. Donc pour envoyer des types de données complexes , vous devez définir explicitement sa mise en page. Dans un langage qui prend en charge la sérialisation en natif, un ensemble de wrappers MPI peut de manière concise utiliser cela; mpi4py for par exemple utilise pickle de python pour envoyer de manière transparente des types de données complexes; mais en C, vous devez retrousser vos manches et le faire vous-même.

Pour votre structure, cela ressemble à ceci:

#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

Bien que la réponse de Jonathan Dursi soit correcte, elle est trop compliquée. MPI fournit des constructeurs de type plus simples et moins généraux plus adaptés à votre problème. MPI_Type_create_struct est UNIQUEMENT nécessaire lorsque vous avez différents types de base (par exemple, un int et un flottant).

Pour votre exemple, plusieurs meilleures solutions existent:

  • En supposant que les deux entiers sont alignés dans une zone de mémoire contiguë (c'est-à-dire, comme un tableau d'entiers), vous n'avez pas du tout besoin d'un type de données dérivé. Envoyez/recevez simplement deux éléments de type MPI_INT avec l'adresse d'une variable de type car à utiliser comme tampon d'envoi/réception:

    MPI_Send(&send, 2, MPI_INT, dest, tag, MPI_COMM_WORLD);
    MPI_Recv(&recv, 2, MPI_INT, src, tag, MPI_COMM_WORLD, &status);
    
  • Si vous souhaitez utiliser un type de données dérivé (par exemple, pour plus de lisibilité ou pour le plaisir), vous pouvez utiliser MPI_Type_contiguous qui correspond aux tableaux:

    MPI_Type_contiguous(2, MPI_INT, &mpi_car_type);
    
  • Dans le cas où les deux entiers sont alignés différemment (ce n'est probablement pas le cas, mais cela dépend de la machine et MPI existent pour de nombreuses plates-formes différentes), vous pouvez utiliser MPI_Type_indexed_block: Il faut un tableau de déplacements (comme MPI_Type_create_struct), mais un seul argument de type ancien et la longueur de bloc de chaque bloc est 1 par définition:

    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);
    

Bien que l'autre solution soit sémantiquement correcte, elle est beaucoup plus difficile à lire et peut entraîner une importante pénalité de performances.

12
mort

Regarder MPI_Type_create_struct pour construire un type de données personnalisé MPI pour votre objet. Un exemple d'utilisation est à http://beige.ucs.indiana.edu/I590/node100.html .

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

OpenMPI enverra count * sizeof(datatype) octets contigus à partir de buf pour permettre l'envoi de choses comme des tableaux int. Par exemple, si vous déclarez un tableau de 10 int int arr[10], Vous pouvez envoyer avec

MPI_Send(arr, 10, MPI_INT, 1, 0, MPI_COMM_WORLD);

et recevoir de la même manière. Puisque buf est un pointeur vide, nous pouvons en abuser pour envoyer des structures en envoyant sizeof(my_struct) octets et en les retransmettant en tant que struct à la réception. Voici un exemple:

#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();
}

Étant donné que les tableaux C stockent les données de manière contiguë, nous pouvons même envoyer des tableaux de structures de la même manière que nous malloc un tableau de structures . Donc, si vous aviez un my_struct m_array[10], Vous enverriez (et recevriez de la même manière) avec

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