serializacja struct w C i transfer przez MPI

Zdefiniowałem Niestandardowy struct, który muszę wysłać do innego Proces MPI przy użyciu MPI_Bsend (lub MPI_Send).

Oto struktura:

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

Problem polega na tym, że oprócz prymitywnych typów MPI nie wydaje się wspierać bezpośredniej "transmisji" złożonych typów danych, takich jak przedstawiona powyżej struktura. Słyszałem, że będę musiał użyć "serializacji".

Jak powinienem podejść do tego i pomyślnie wysłać myCar, aby przetworzyć 5?

 33
Author: kstratis, 2012-03-25

4 answers

Jeremiasz ma rację-MPI_Type_create_struct jest drogą do tego.

Ważne jest, aby pamiętać, że MPI jest biblioteką, nie wbudowaną w Język; więc nie może "zobaczyć", jak wygląda struktura, aby ją serializować. Aby wysyłać złożone typy danych, musisz jawnie zdefiniować jego układ. W języku, który ma natywne wsparcie dla serializacji, zestaw wrapperów MPI może z tego korzystać; mpi4py na przykład korzysta z Pythona pickle aby transparentnie wysyłać złożone typy danych; ale w C, musisz zakasać rękawy i zrobić to sam.

Dla twojej struktury wygląda to tak:

#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;
}
 63
Author: Jonathan Dursi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-26 02:04:01

Chociaż odpowiedź Jonathana Dursiego jest poprawna, jest zbyt skomplikowana. MPI zapewnia prostsze i mniej ogólne konstruktory typu bardziej odpowiednie dla Twojego problemu. MPI_Type_create_struct jest potrzebna tylko wtedy, gdy masz różne typy bazowe(np. int i float).

Dla Twojego przykładu istnieje kilka lepszych rozwiązań:

  • Zakładając, że obie liczby całkowite są wyrównane w sąsiednim obszarze pamięci (np. jak tablica liczb całkowitych), nie potrzebujesz w ogóle pochodnego typu danych. Just wysyłanie / odbieranie dwóch elementów typu MPI_INT z adresem zmiennej typu car, które mają być użyte jako bufor wysyłania/odbierania:

    MPI_Send(&send, 2, MPI_INT, dest, tag, MPI_COMM_WORLD);
    MPI_Recv(&recv, 2, MPI_INT, src, tag, MPI_COMM_WORLD, &status);
    
  • Jeśli chcesz użyć pochodnego typu danych (np. dla czytelności lub zabawy), możesz użyć MPI_Type_contiguous, która odpowiada tablicom:

    MPI_Type_contiguous(2, MPI_INT, &mpi_car_type);
    
  • W przypadku, gdy dwie liczby całkowite są wyrównane inaczej (najprawdopodobniej nie, ale jest to zależne od Maszyny i implementacje MPI istnieją dla wielu różnych platform), możesz użyć MPI_Type_indexed_block: pobiera tablicę przemieszczeń (jak MPI_Type_create_struct), ale tylko jeden argument oldtype i długość bloku każdego bloku wynosi 1 z definicji:

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

Podczas gdy inne rozwiązanie jest semantycznie poprawne, jest o wiele trudniejsze do odczytania i może pociągnąć za sobą dużą karę za wydajność.

 13
Author: mort,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-30 12:06:44

Spójrz na MPI_Type_create_struct aby zbudować własny typ danych MPI dla Twojego obiektu. Przykład użycia znajduje się w http://beige.ucs.indiana.edu/I590/node100.html .

 6
Author: Jeremiah Willcock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-25 22:13:06
int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)

OpenMPI wyśle count * sizeof(datatype) sąsiadujące ze sobą bajty zaczynające się od buf, aby umożliwić wysyłanie takich rzeczy jak tablice int. Na przykład, jeśli zadeklarujesz tablicę 10 int int arr[10], możesz wysłać za pomocą

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

I otrzymać podobnie. Ponieważ buf jest wskaźnikiem void, możemy to wykorzystać do wysyłania struktur poprzez wysyłanie sizeof(my_struct) bajtów i odlewanie z powrotem jako struktury na końcu odbiorczym. Oto przykład:

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

Ponieważ tablice C przechowują dane w sposób ciągły, możemy nawet wysyłać tablice struktur w podobny sposób jak malloc tablica struktur. Jeśli więc masz my_struct m_array[10], wysyłasz (i odbierasz podobnie) za pomocą

MPI_Send(m_array, sizeof(my_struct) * 10, MPI_CHAR, 1, 0, MPI_COMM_WORLD);
 1
Author: qwr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-31 22:25:40