430 likes | 588 Views
Podstawy MPI-2: część I. Literatura: Using MPI-2. Advanced Features of the Message-Passing Interface , W. Gropp, E. Lusk, R. Thakur, MIT Press, Cambridge, 1999. Przykłady są dostępne na sieci pod http://www-unix.mcs.anl.gov/mpi/usingmpi2/
E N D
Podstawy MPI-2: część I • Literatura: • Using MPI-2. Advanced Features of the Message-Passing Interface, W. Gropp, E. Lusk, R. Thakur, MIT Press, Cambridge, 1999. Przykłady są dostępne na sieci pod • http://www-unix.mcs.anl.gov/mpi/usingmpi2/ • Prezentacja “Introduction to parallel I/O and MPI-IO”, R. Thakur, ANL • Tutorial “Rozszerzenia standardu MPI” na stronie Poznańskiego Centrum Sieciowo-Superkomupterowego. • MPI Forum (http://www.mpi-forum.org)
Historia • 1992: Pierwsze spotkanie MPI Forum na konferencji Supercomputing ‘92. Początek prac nad standardem MPI-1. • 1994: Pierwsze wydanie książki “Using MPI” pomaga przekonać programistów równoległych do tego standardu. Użytkownicy PVM wskazują jednak na brak możliwości dynamicznej alokacji procesów, wszyscy użytkownicy wskazują na konieczność równoległych operacji I/O a CRAY-T3D (biblioteka shmem) i CM-5 udowadniają użyteczność jednostronnych operacji na pamięci. • 1995: spotkanie MPI-2 Forum i ponowne podjęcie prac nad standardem MPI. Trochę zmodyfikowany standard MPI-1.1 • 1997: MPI-2 dostępna dla użytkowników.
Nowe rzeczy w MPI-2 Główne modyfikacje (“wielka trójka”) • Równoległe operacje wejścia/wyjścia. • Operacje na pamięci odległej. • Dynamiczne zarządzanie procesami Pomniejsze modyfikacje • Specyfikacje zewnętrznych interfejsów. • Połączenie z C++ i Fortranem 90. • Wątki. • Łączenie modułów w różnych językach. • Rozszerzone operacje komunikacji zbiorowej (np. na interkomunikatorach).
Równoległe operacje wejścia/wyjścia MPI-IO • Pisanie do pliku jest wysłaniem wiadomości do systemu plików; narzędzia do tego celu już są w MPI. • Podstawowe funkcje MPI-IO: • open, • close, • seek, • read, • write Argumenty tych funkcji są podobne jak w UNIXowym I/O.
Cechy operacji MPI-IO • Rozproszenie pamięci oraz rekordów pliku. • Zbiorowe operacje I/O. • Niewstrzymuące operacje I/O. • Specyfikacja offsetu położenia rekordu w pliku w celu uniknięcia oddzielnych operacji seek. • Lokalne i globalne wskaźniki plików. • Reprezentacje danych przenaszalne i możliwe do definiowania przez użytkownika. • Wskazówki dla implementacji i systemu plików.
Operacje na pamięci odległej • Model przesyłania wiadomości: dane są przesyłane przy użyciu pary send/receive. • Model pamięci wspólnej: procesory mają dostęp do wspólnej części pamięci i mogą pobierać lub umieszczać w niej dane. • W MPI-2 zdefiniowano operacje put, get i collect, które umożliwiają dostęp do pamięci innego procesora w sposób analogiczny do modelu pamięci wspólnej. Te operacje określa się mianem operacji na pamięci odległej, zdalnych operacji na pamięci lub jednostronnych operacji na pamięci (ang. “one-sided” lub “remote memory” operations). • Pamięć dostępną dla innego procesora specyfikuje się jako okno pamięci (window).
Cechy operacji na pamięci odległej w MPI-2 • Zrównoważenie efektywności i przenaszalności między różnymi architekturami, włączając maszyny SMP (Shared Memory multiProcessors), NUMA (NonuUiform Memory Access), MPP (distributed memory Massively Parallel Processors), klastry SMP, czy sieci heterogeniczne. • Zachowanie “spojrzenia i czucia” (look and feel) MPI. • Uwzględnienie subtelnych różnic zachowania pamięci takich, jak koherencja pamięci cache, zgodność sekwencyjna, itp. • Oddzielenie synchronizacji od przesyłania danych w celu zwiększenia efektywności.
Dynamiczne zarządzanie procesami • W MPI-2 dany proces może • uczestniczyć w tworzeniu nowego procesu MPI (operacja rozmnażania; ang. spawning), • nawiązać komunikację z procesami wystartowanymi oddzielnie (operacja połączenia). • Główe cele przy projektowaniu takiego API (Application Programing Interface): • Utrzymanie prostoty i elastyczności. • Interakcja z systemem operacyjnym, zarządcą zasobów, procesu oraz złożonym otoczeniem programowym. • Unikanie efektu “wyścigu” źle wpływającego na dokładność. • Aby osiągnąć te cele, operacje zarządzania pamięcią muszą być zbiorowe, zarówno w obrębie procesów macierzystych jak i potomych. Procesy potomne są reprezentowane przez interkomunikatory.
Sekwencyjne I/O w programie równoległym Master Pamięć Procesor plik Pisze/czyta tylko master, który odpowiednio zbiera dane od innych procesorów lub rozsyła do nich przeczytane dane.
Przykład pisania tylko przez mastera MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); MPI_Comm_size(MPI_COMM_WORLD, &numprocs); for (i=0; i<BUFSIZE; i++) buf[i] = myrank * BUFSIZE + i; if (myrank != 0) MPI_Send(buf, BUFSIZE, MPI_INT, 0, 99, MPI_COMM_WORLD); else { myfile = fopen("testfile", "w"); fwrite(buf, sizeof(int), BUFSIZE, myfile); for (i=1; i<numprocs; i++) { MPI_Recv(buf, BUFSIZE, MPI_INT, i, 99, MPI_COMM_WORLD, &status); fwrite(buf, sizeof(int), BUFSIZE, myfile); } fclose(myfile); }
Zalety takiej organizacji pisania/czytania • Maszyna na której chodzi program równoległy może dopuszczać I/O tylko z jednego albo ograniczonej liczby procesów lub znacznie ograniczać wydajność jeżeli każdy procesor pisze do oddzielnego pliku na wspólnym systemie plików (np. bigben w PSC czy galera w TASKu). • Można używać zaawansowanych nierównoległych bibliotek I/O (np. biblioteki zarządzania danymi). • Wszystkie wyniki są w jednym pliku, na którym takie operacje jak ftp, cp, mv są łatwiejsze i szybsze niż na wielu. Wady • Obniżanie efektywności kodu poprzez sekwencyjność operacji pisania/czytania i konieczność przesyłania danych.
Równoległe nie-MPI I/O w programie równoległym Pamięć Procesor plik Każdy procesor pisze do oddzielnego pliku albo niezależnie czyta z tego samego lub oddzielnego pliku
Przykład pisania przez każdy procesor do oddzielnego pliku o nazwie zawierającej rząd procesora MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); for (i=0; i<BUFSIZE; i++) buf[i] = myrank * BUFSIZE + i; sprintf(filename, "testfile.%d", myrank); myfile = fopen(filename, "w"); fwrite(buf, sizeof(int), BUFSIZE, myfile); fclose(myfile); MPI_Finalize(); return 0; W FORTRANie rząd procesora można umieścić w nazwie pliku przez pisanie do łańcucha, np. character*64 fname …….. write (fname,'(2a,bz,i3.3)') ‘plik_',taskid
Zalety takiego podejścia • Operacje I/O są równoległe ale mogą korzystać z sekwencyjnych bibliotek I/O. Wady • Często pliki wyprodukowane przez poszczególne procesory trzeba łączyć w jeden do dalszego przetwarzania. • Program używających te pliki jako pliki danych często sam musi być równoległy i chodzić na tej samej liczbie procesorów co program, który je wyprodukował. • Uciążliwość w “ogarnięciu” i wykonywaniu opearacji takich jak cp, mv, ftp na dziesiątkach a nawet tysiącach małych plików. • Pojawianie się nieoczekiwanych błędów przy jednoczesnym czytaniu jednego dużego pliku danych przez wiele procesorów.
Kod piszący wyniki do wielu plików z użyciem MPI-IO MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); for (i=0; i<BUFSIZE; i++) buf[i] = myrank * BUFSIZE + i; sprintf(filename, "testfile.%d", myrank); MPI_File_open(MPI_COMM_SELF, filename, MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL, &myfile); MPI_File_write(myfile, buf, BUFSIZE, MPI_INT, MPI_STATUS_IGNORE); MPI_File_close(&myfile); MPI_Finalize();
MPI_FILE_OPEN(comm, filename, amode, info, fh) [ IN comm] komunikator (handle) [ IN filename] nazwa pliku (string) [ IN amode] tryb dostępu do pliku (integer) [ IN info] obiekt info (handle) [ OUT fh] “pokrętło” utworzonego pliku (handle) C: int MPI_File_open(MPI_Comm comm, char *filename, int amode, MPI_Info info, MPI_File *fh) FORTRAN/FORTRAN 90: MPI_FILE_OPEN(COMM, FILENAME, AMODE, INFO, FH, IERROR)CHARACTER*(*) FILENAME INTEGER COMM, AMODE, INFO, FH, IERROR C++:static MPI::File MPI::File::Open(const MPI::Intracomm& comm, const char* filename, int amode, const MPI::Info& info)
MPI_FILE_WRITE(fh, buf, count, datatype, status) [ INOUT fh] “pokrętło” pliku (handle) [ IN buf] początkowy adres buforu (choice) [ IN count] liczba elementów danych w buforze (integer) [ IN datatype] typ każdego elementu danych (handle) [ OUT status] obiekt stanu (Status) C: int MPI_File_write(MPI_File fh, void *buf, int count, MPI_Datatype datatype, MPI_Status *status) FORTRAN/FORTRAN90: MPI_FILE_WRITE(FH, BUF, COUNT, DATATYPE, STATUS, IERROR) <type> BUF(*) INTEGER FH, COUNT, DATATYPE, STATUS(MPI_STATUS_SIZE), IERROR C++: void MPI::File::Write(const void* buf, int count, const MPI::Datatype& datatype, MPI::Status& status) void MPI::File::Write(const void* buf, int count, const MPI::Datatype& datatype)
MPI_FILE_CLOSE(fh) [ INOUT fh] “pokrętło” pliku (handle) C: int MPI_File_close(MPI_File *fh) FORTRAN/FORTRAN90: MPI_FILE_CLOSE(FH, IERROR)INTEGER FH, IERROR C++: void MPI::File::Close()
Schemat równoległego pisania/czytania z tego samego pliku przez wiele procesorów. Każdy procesor ma w danej chwili dostęp do innej części pliku. Pamięć Procesor plik
Kod piszący wyniki do jednego pliku z użyciem MPI-IO: C MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); for (i=0; i<BUFSIZE; i++) buf[i] = myrank * BUFSIZE + i; MPI_File_open(MPI_COMM_WORLD, "testfile", MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile); MPI_File_set_view(thefile, myrank * BUFSIZE * sizeof(int), MPI_INT, MPI_INT, "native", MPI_INFO_NULL); MPI_File_write(thefile, buf, BUFSIZE, MPI_INT, MPI_STATUS_IGNORE); MPI_File_close(&thefile); MPI_Finalize();
Kod piszący wyniki do jednego pliku z użyciem MPI-IO: FORTRAN90 call MPI_INIT(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD, myrank, ierr) do i = 0, BUFSIZE buf(i) = myrank * BUFSIZE + i enddo call MPI_FILE_OPEN(MPI_COMM_WORLD, 'testfile', & MPI_MODE_WRONLY + MPI_MODE_CREATE, & MPI_INFO_NULL, thefile, ierr) ! assume 4-byte integers disp = myrank * BUFSIZE * 4 call MPI_FILE_SET_VIEW(thefile, disp, MPI_INTEGER, & MPI_INTEGER, 'native', & MPI_INFO_NULL, ierr) call MPI_FILE_WRITE(thefile, buf, BUFSIZE, MPI_INTEGER, & MPI_STATUS_IGNORE, ierr) call MPI_FILE_CLOSE(thefile, ierr) call MPI_FINALIZE(ierr)
MPI_FILE_SET_VIEW(fh, disp, etype, filetype, datarep, info) [ INOUT fh] “pokrętło” pliku (handle) [ IN disp] przesunięcie (pozycja początkowego rekordu) (integer) [ IN etype] elementarny typ danych (handle) [ IN filetype] typ pliku (handle) [ IN datarep] reprezentacja danych (string) [ IN info] obiekt informacyjny (handle) C: int MPI_File_set_view(MPI_File fh, MPI_Offset disp, MPI_Datatype etype, MPI_Datatype filetype, char *datarep, MPI_Info info) FORTRAN/FORTRAN90 MPI_FILE_SET_VIEW(FH, DISP, ETYPE, FILETYPE, DATAREP, INFO, IERROR)INTEGER FH, ETYPE, FILETYPE, INFO, IERROR CHARACTER*(*) DATAREP INTEGER(KIND=MPI_OFFSET_KIND) DISP C++void MPI::File::Set_view(MPI::Offset disp, const MPI::Datatype& etype, const MPI::Datatype& filetype, const char* datarep, const MPI::Info& info)
Czytanie pliku przez inną liczbę procesorów niż użyta to jego zapisu MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); MPI_Comm_size(MPI_COMM_WORLD, &numprocs); MPI_File_open(MPI_COMM_WORLD, "testfile", MPI_MODE_RDONLY, MPI_INFO_NULL, &thefile); MPI_File_get_size(thefile, &filesize); /* in bytes */ filesize = filesize / sizeof(int); /* in number of ints */ bufsize = filesize / numprocs + 1; /* local number to read */ buf = (int *) malloc (bufsize * sizeof(int)); MPI_File_set_view(thefile, myrank * bufsize * sizeof(int), MPI_INT, MPI_INT, "native", MPI_INFO_NULL); MPI_File_read(thefile, buf, bufsize, MPI_INT, &status); MPI_Get_count(&status, MPI_INT, &count); printf("process %d read %d ints\n", myrank, count); MPI_File_close(&thefile); MPI_Finalize();
MPI_FILE_GET_SIZE(fh, size) [IN fhfile] “pokrętło” pliku (handle) [OUT size] rozmiar pliku w bajtach (integer) C: int MPI_File_get_size(MPI_File fh, MPI_Offset *size) FORTRAN/FORTRAN90: MPI_FILE_GET_SIZE(FH, SIZE, IERROR)INTEGER FH, IERROR INTEGER(KIND=MPI_OFFSET_KIND) SIZE C++ MPI::Offset MPI::File::Get_size() const
MPI_FILE_READ(fh, buf, count, datatype, status) [INOUT fh] “pokrętło” pliku (handle) [OUT buf] początkowy adres buforu (choice) [IN count] liczba elementów buforu (integer) IN datatype datatype of each buffer element (handle) [OUT status] obiekt stanu (Status) C: int MPI_File_read(MPI_File fh, void *buf, int count, MPI_Datatype datatype, MPI_Status *status) FORTRAN/FORTRAN90: MPI_FILE_READ(FH, BUF, COUNT, DATATYPE, STATUS, IERROR) <type> BUF(*) INTEGER FH, COUNT, DATATYPE, STATUS(MPI_STATUS_SIZE), IERROR C++: void MPI::File::Read(void* buf, int count, const MPI::Datatype& datatype, MPI::Status& status) void MPI::File::Read(void* buf, int count, const MPI::Datatype& datatype)
Wydruk z programu czytającego bufor (dla 2 procesorów) mpirun -np 2 ./io35p | sort -n -k 1 process 0 read 5 ints process 1 read 3 ints processor 0 buf[0]=0 processor 0 buf[1]=1 processor 0 buf[2]=2 processor 0 buf[3]=3 processor 0 buf[4]=4 processor 1 buf[0]=5 processor 1 buf[1]=6 processor 1 buf[2]=7 Wydruk z programu piszącego bufor (dla BUFSIZE=2 i 4 procesorów) mpirun -np 4 ./io3p | sort -n -k 1 Processor 0 buf[0]=0 Processor 0 buf[1]=1 Processor 1 buf[0]=2 Processor 1 buf[1]=3 Processor 2 buf[0]=4 Processor 2 buf[1]=5 Processor 3 buf[0]=6 Processor 3 buf[1]=7
Inne sposoby pisania do pliku wspóldzielonego • MPI_File_seek • MPI_File_read_at • MPI_File_write_at • MPI_File_read_shared • MPI_File_write_shared Wszystkie te operacje są operacjami komunikacji zbiorowej
Niewstrzymujące instrukcje czytania/pisania Standardowe operacje read i write są operacjami wstrzymującymi jak standardowe send i receive ale można użyć • MPI_File_iread • MPI_File_iwrite Żeby upewnić się, że pliki zostały zapisane/przeczytane należy dodać w odpowiednim miejscu instrukcję MPI_Wait tak jak w przypadku niewstrzymującego send lub receive.
Zbiorowe instrukcje czytania/pisania Odpowiednikami operacji komunikacji zbiorowej są • MPI_File_read_all • MPI_File_write_all Pierwszą operacją można porównać do broadcast a drugą do gather.
Okno dostępu do pamięci: część pamięci adresowej danego procesora udostępnionej dla innych procesorów będących w danym komunikatorze. • Utworzenie okna dostępu do pamięci jest operacją komunikacji zbiorowej. • Okna dostępu do pamięci tworzą rozproszony obiekt złożony z częśći pamięci procesorów dostępnych dla innych. • W obrębie okna są dostępne operacje: • get: uzyskiwanie danych z okna pamięci, • put: umieszczanie danych w oknie pamięci, • accumulate: modyfikacja danych w oknie dostępu do pamięci (np. dodawanie nowego składnika do sumy). Te operacje są niewstrzymujące i wymagają synchronizacji (fence).
Ilustracja operacji na pamięci odległej Procesor 0 Procesor 1 get put Lokalna przestrzeń adresowa Okna dostępu do pamięci odległej
Dla ilustracji rozważymy jeszcze raz obliczanie liczby p przez całkowanie numeryczne pochodnej funkcji arcus tangens metodą trapezów. Ilustracja przybliżonego obliczania liczby p przez całkowanie numeryczne dla liczby przedziałów n=10.
Zrównoleglenie algorytmu • Przedział całkowania dzieli się na n części: 1, 2,..., n • W układzie m procesorów, procesor 1 sumuje wkłady dla części 1, m+1, procesor 2 dla 2, m+2itd. • Procesory przesyłają swoje obliczone cząstkowe sumy do mastera, który oblicza sumę całkowitą. Źródło programu z użyciem MPI-1 (operacje broadcast i reduce)(C) Źrodło programu z użyciem MPI-2 (operacje na pamięci odległej) (C)
Porównanie kodu w MPI-1 (po lewej) z kodem w MPI-2 (po prawej) MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); if (myid == 0) { MPI_Win_create(&n, sizeof(int), 1, MPI_INFO_NULL,MPI_COMM_WORLD, &nwin); MPI_Win_create(&pi, sizeof(double), 1, MPI_INFO_NULL,MPI_COMM_WORLD, &piwin); } else { MPI_Win_create(MPI_BOTTOM, 0, 1, MPI_INFO_NULL,MPI_COMM_WORLD, &nwin); MPI_Win_create(MPI_BOTTOM, 0, 1, MPI_INFO_NULL,MPI_COMM_WORLD, &piwin); }
while (!done) { if (myid == 0) { printf("Enter the number of intervals: (0 quits) "); scanf("%d",&n); } MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); if (n == 0) break; h = 1.0 / (double) n; sum = 0.0; for (i = myid + 1; i <= n; i += numprocs) { x = h * ((double)i - 0.5); sum += 4.0 / (1.0 + x*x); } mypi = h * sum; while (1) { if (myid == 0) { printf("Enter the number of intervals: (0 quits) "); scanf("%d",&n); pi = 0.0; } MPI_Win_fence(0, nwin); if (myid != 0) MPI_Get(&n, 1, MPI_INT, 0, 0, 1, MPI_INT, nwin); MPI_Win_fence(0, nwin); if (n == 0) break; else { h = 1.0 / (double) n; sum = 0.0; for (i = myid + 1; i <= n; i += numprocs) { x = h * ((double)i - 0.5); sum += (4.0 / (1.0 + x*x)); } mypi = h * sum;
MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (myid == 0) printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - PI25DT)); } MPI_Finalize(); MPI_Win_fence( 0, piwin); MPI_Accumulate(&mypi, 1, MPI_DOUBLE, 0, 0, 1, MPI_DOUBLE, MPI_SUM, piwin); MPI_Win_fence(0, piwin); if (myid == 0) printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - PI25DT)); } } MPI_Win_free(&nwin); MPI_Win_free(&piwin); MPI_Finalize();
MPI_WIN_CREATE(base, size, disp_unit, info, comm, win) [IN base] początkowy adres okna (choice) [IN size] rozmiar okna w bajtach (integer, nieujemny) [IN disp_unit] lokalna jednostka przesunięcia w bajtach (integer, dodatni) [IN info] “pokrętło” obiektu informacyjnego (handle) [IN comm] komunikator (handle) [OUT win] “pokrętło” do okna (handle) C: int MPI_Win_create(void *base, MPI_Aint size, int disp_unit, MPI_Info info, MPI_Comm comm, MPI_Win *win) FORTRAN/FORTRAN90: MPI_WIN_CREATE(BASE, SIZE, DISP_UNIT, INFO, COMM, WIN, IERROR)<type> BASE(*) INTEGER(KIND=MPI_ADDRESS_KIND) SIZE INTEGER DISP_UNIT, INFO, COMM, WIN, IERROR C++: static MPI::Win MPI::Win::Create(const void* base, MPI::Aint size, int disp_unit, const MPI::Info& info, const MPI::Intracomm& comm)
MPI_GET(origin_addr, origin_count, origin_datatype, target_rank, target_disp, target_count, target_datatype, win) [OUT origin_addr] początkowy adres początkowego bufora (choice) [IN origin_count] liczba elementów początkowego bufora (integer, nieujemny) [IN origin_datatype] typ danych każdego elementu (handle) [IN target_rank] rząd procesora celowego (integer, nieujemny) [IN target_disp] przesunięcie okna w stosunku do bufora celowego (integer, nieujemny) [IN target_count] liczba elementów bufora celowego (integer, nieujemny) [IN target_datatype] typ danych poszczególnych elementów bufora celowego (handle) [IN win] obiekt okna używany w komunikacji (handle)
C: int MPI_Get(void *origin_addr, int origin_count, MPI_Datatype origin_datatype, int target_rank, MPI_Aint target_disp, int target_count, MPI_Datatype target_datatype, MPI_Win win) FORTRAN/FORTRAN90 MPI_GET(ORIGIN_ADDR, ORIGIN_COUNT, ORIGIN_DATATYPE, TARGET_RANK, TARGET_DISP, TARGET_COUNT, TARGET_DATATYPE, WIN, IERROR)<type> ORIGIN_ADDR(*) INTEGER(KIND=MPI_ADDRESS_KIND) TARGET_DISP INTEGER ORIGIN_COUNT, ORIGIN_DATATYPE, TARGET_RANK, TARGET_COUNT, TARGET_DATATYPE, WIN, IERROR C++: void MPI::Win::Get(const void *origin_addr, int origin_count, const MPI::Datatype& origin_datatype, int target_rank, MPI::Aint target_disp, int target_count, const MPI::Datatype& target_datatype) const
MPI_WIN_FENCE(assert, win) [IN assert] asercja programowa (integer) [IN win] obiekt okna (handle) C: int MPI_Win_fence(int assert, MPI_Win win) FORTRAN/FORTRAN90: MPI_WIN_FENCE(ASSERT, WIN, IERROR)INTEGER ASSERT, WIN, IERROR C++: void MPI::Win::Fence(int assert) const
MPI_WIN_FREE(win) [INOUT win] obiekt okna (handle) C: int MPI_Win_free(MPI_Win *win) FORTRAN/FORTRAN90: MPI_WIN_FREE(WIN, IERROR)INTEGER WIN, IERROR C++: void MPI::Win::Free()