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?
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;
}
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.
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 .
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);