Wysyłanie kolumn macierzy za pomocą MPI Scatter

Próbuję napisać program do mnożenia macierzy i wektorów za pomocą MPI. Próbuję wysłać kolumny macierzy do oddzielnych procesów i lokalnie obliczyć wynik. Na koniec wykonuję MPI_Reduce za pomocą operacji MPI_SUM.

Wysyłanie wierszy macierzy jest łatwe, ponieważ C przechowuje tablice w rzędzie-głównym porządku, ale kolumny nie są (jeśli nie wysyłasz ich jeden po drugim). Przeczytałem pytanie tutaj:

MPI_Scatter-wysyłanie kolumn tablicy 2D

Jonathan Dursi zasugerował użycie nowego Typy danych MPI i oto co zrobiłem dostosowując jego kod do własnych potrzeb: {]}
  double matrix[10][10];
  double mytype[10][10];
  int part_size; // stores how many cols a process needs to work on
  MPI_Datatype col, coltype;
  // ...
  MPI_Type_vector(N, 1, N, MPI_DOUBLE, &col);
  MPI_Type_commit(&col);
  MPI_Type_create_resized(col, 0, 1*sizeof(double), &coltype);
  MPI_Type_commit(&coltype);
  // ...
  MPI_Scatter(matrix, part_size, coltype,
              mypart, part_size, coltype,
              0, MPI_COMM_WORLD);

  // calculations...
  MPI_Reduce(local_result, global_result,
             N, MPI_DOUBLE,
             MPI_SUM,
             0, MPI_COMM_WORLD);
To działa idealnie, ale nie mogę powiedzieć, że naprawdę rozumiem, jak to działa.
  1. w jaki sposób MPI_Type_vector jest przechowywany w pamięci?
  2. Jak działa MPI_Type_create_resized() i co dokładnie robi?

Proszę pamiętać, że jestem początkujący w MPI. Z góry dzięki.

 10
Author: Community, 2012-05-28

1 answers

Jest długi opis tego problemu w moja odpowiedź na to pytanie : fakt, że wiele osób ma te pytania, jest dowodem na to, że nie jest to oczywiste, a pomysły wymagają przyzwyczajenia.

Ważne jest, jaki układ pamięci opisuje typ danych MPI. Ciąg wywołujący do MPI_Type_vector to:

int MPI_Type_vector(int count,
                   int blocklength,
                   int stride, 
                   MPI_Datatype old_type,
                   MPI_Datatype *newtype_p)

Tworzy nowy typ, który opisuje układ pamięci, w którym co stride elementów, jest wyciągnięty blok blocklength elementów, i łącznie count tych bloków. Przedmioty są w jednostkach tego, co old_type było. Na przykład, jeśli wywołałeś (nazwałeś parametry tutaj, czego nie możesz zrobić w C, ale:)

 MPI_Type_vector(count=3, blocklength=2, stride=5, old_type=MPI_INT, &newtype);

Wtedy newtype opisze układ w pamięci tak:

   |<----->|  block length

   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
   | X | X |   |   |   | X | X |   |   |   | X | X |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   |<---- stride ----->|

   count = 3

Gdzie każdy kwadrat to jeden całkowity kawałek pamięci, przypuszczalnie 4 bajty. Należy zauważyć, że krok jest odległością w liczbach całkowitych od początku jednego bloku do początku następnego, a nie odległość między bloki.

Ok, więc w Twoim przypadku zadzwoniłeś

  MPI_Type_vector(N, 1, N, MPI_DOUBLE, &col);

Który zajmie count = N bloki, każdy o rozmiarze blocklength=1 MPI_DOUBLEs, z odstępem między początkiem każdego bloku stride=N MPI_DOUBLEs. innymi słowy, zajmie to każde N ' - te podwójne, łącznie N razy; idealne do wydobywania jednej kolumny z (sąsiadująco przechowywanej) tablicy sobowtórów NxN. Przydatnym sprawdzeniem jest sprawdzenie, ile danych jest przetaczanych (count*stride = N*N czyli pełny rozmiar macierzy, sprawdź) i ile danych jest faktycznie included (count*blocksize = N, czyli wielkość kolumny, sprawdź.)

Gdyby wystarczyło wywołać MPI_Send i MPI_Recv, aby wymienić poszczególne kolumny, byłoby po sprawie; można by użyć tego typu do opisania układu kolumny i byłoby dobrze. Ale jest jeszcze jedna rzecz.

Chcesz wywołać MPI_Scatter, które wysyła pierwszy coltype (powiedzmy) do procesora 0, następny coltype do procesora 1, itd. Jeśli robisz to za pomocą prostej tablicy 1d, łatwo jest dowiedzieć się, gdzie są "następne" dane typ jest; jeśli rozpraszasz 1 int do każdego procesora," next " int rozpoczyna się natychmiast po zakończeniu pierwszej int.

Ale twoja nowa kolumna coltype ma całkowityzakres , który przechodzi od początku kolumny do N*N MPI_DOUBLES później -- jeśli MPI_Scatter podąża za tą samą logiką (robi), zacznie szukać kolumny "next" poza pamięcią macierzy, i tak dalej z next i next. Nie tylko nie otrzymasz odpowiedzi, którą chciałeś, program prawdopodobnie crash.

Aby to naprawić, należy powiedzieć MPI, że "rozmiar" tego typu danych do celów obliczenia, gdzie znajduje się "następny", jest rozmiarem w pamięci między miejscem, w którym zaczyna się jedna kolumna, a rozpoczyna się następna kolumna, czyli dokładnie jedna MPI_DOUBLE. Nie ma to wpływu na ilość przesłanych danych, które nadal są warte 1 kolumny danych; wpływa tylko na obliczenie "następny w linii". Z kolumnami (lub wierszami) w tablicy, możesz po prostu wysłać ten rozmiar, aby był odpowiedni rozmiar kroku w pamięci, a MPI wybierze właściwą następną kolumnę do wysłania. Bez tego operatora zmiany rozmiaru Twój program prawdopodobnie ulegnie awarii.

Jeśli masz bardziej skomplikowane układy danych, jak w 2d-blockach przykładu 2D-array powiązanego z powyższym, to nie ma jednego kroku między "kolejnymi" elementami; nadal musisz wykonać sztuczkę zmiany rozmiaru, aby Rozmiar był użyteczną jednostką, ale musisz użyć mpi_scatterv zamiast rozpraszać, aby jawnie określić lokalizacje do wysłania od.

 32
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
2017-05-23 11:54:18