MPI to standardowy protokół komunikacyjny służący do wymieniania danych między procesami programów równoległych, używany przede wszystkim w obliczeniach superkomputerowych. Celem zadania, jak sugeruje nazwa MIMPI - będąca skrótem od My Implementation of MPI - jest zaimplementowanie drobnego, nieco zmodyfikowanego fragmentu MPI. Należy napisać według poniższej specyfikacji zarówno:
- kod programu
mimpirun
(wmimpirun.c
) uruchamiający obliczenia równoległe, - implementację
mimpi.c
procedur zadeklarowanych wmimpi.h
.
Program mimpirun
przyjmuje następujące argumenty linii poleceń:
-
$n$ - liczba kopii do uruchomienia (można założyć, że przekazana zostanie liczba naturalna z zakresu od 1 do$16$ włącznie) -
$prog$ - ścieżka do pliku wykonywalnego (może się znajdować w PATH). W przypadku, gdy odpowiednie wywołanieexec
się nie powiedzie (np. z powodu niepoprawnej ścieżki) należy zakończyć działaniemimpirun
z niezerowym kodem wyjściowym. -
$args$ - opcjonalnie i w dowolnej ilości argumenty do przekazania wszystkim uruchamianym programom$prog$
Program mimpirun
po kolei (następna czynność jest rozpoczynana po całkowitym zakończeniu poprzedniej):
- Przygotowuje środowisko (w zakresie implementującego jest zdecydowanie, co to znaczy).
- Uruchamia
$n$ kopii programu$prog$ , każdą w osobnym procesie. - Czeka na zakończenie wszystkich utworzonych procesów.
- Kończy działanie.
- Programy
$prog$ mogą jednokrotnie w czasie swojego działania przejść do bloku MPI. Aby tego dokonać wywołują na początku funkcję bibliotecznąMIMPI_Init
, a na jego końcu wykonują funkcję bibliotecznąMIMPI_Finalize
. Jako wspomniany blok MPI rozumie się cały fragment kodu pomiędzy w/w wywołaniami. - Będąc w bloku MPI, programy mogą wykonywać różne procedury z biblioteki
mimpi
celem komunikacji z innymi procesami$prog$ . - Mogą wykonywać dowolne operacje (zapis, odczyt, otwarcie, zamknięcie, itp.) na plikach,
których numery deskryptorów są z zakresów
$[0,19]$ i$[1024, \infty)$ (w szczególności naSTDIN
,STDOUT
iSTDERR
). - Nie modyfikują wartości zmiennych środowiskowych zaczynających się od prefiksu
MIMPI
. - Oczekują prawidłowo ustawionych argumentów,
tzn. zerowy argument zgodnie z uniksową konwencją powinien być nazwą programu
$prog$ , natomiast następne argumenty powinny odpowiadać argumentom$args$ . Wskazówka: aby przekazać dodatkowe dane zmimpirun
do$prog$ można posłużyć się funkcjami z rodziny*env
:getenv
,setenv
,putenv
.
Należy zaimplementować następujące procedury o sygnaturach z pliku nagłówkowego mimpi.h
:
-
void MIMPI_Init(bool enable_deadlock_detection)
Otwiera blok MPI, inicjalizując potrzebne dla działania biblioteki
mimpi
zasoby. Flagaenable_deadlock_detection
włącza wykrywanie zakleszczeń do końca bloku MPI (opis poniżej w Usprawnienie4). -
void MIMPI_Finalize()
Kończy blok MPI. Wszystkie zasoby związane z działaniem biblioteki
mimpi
:- otwarte pliki
- otwarte kanały komunikacyjne
- zaalokowana pamięć
- prymitywy synchronizacyjne
- itp.
powinny być zwolnione przed zakończeniem tej procedury.
-
int MIMPI_World_size()
Zwraca liczbę procesów
$prog$ uruchomionych z użyciem programumimpirun
(powinno być równe parametrowi$n$ przekazanemu do wywołaniamimpirun
). -
void MIMPI_World_rank()
Zwraca unikatowy w ramach grupy procesów uruchomionych przez
mimpirun
identyfikator. Identyfikatory powinny być kolejnymi liczbami naturalnymi od$0$ do$n-1$ .
-
MIMPI_Retcode MIMPI_Send(void const *data, int count, int destination, int tag)
Wysyła dane spod adresu
data
, interpretując je jako tablicę bajtów o wielkościcount
, do procesu o randzedestination
, opatrując wiadomość znacznikiemtag
.Wykonanie
MIMPI_Send
na rzecz procesu, który już opuścił blok MPI powinno zakończyć się natychmiastowym niepowodzeniem zwracając kod błęduMIMPI_ERROR_REMOTE_FINISHED
. Nie należy przejmować się sytuacją, w której proces na rzecz którego wykonane zostałoMIMPI_Send
zakończy działanie później (po pomyślnym zakończeniu funkcjiMIMPI_send
w procesie wysyłającym). -
MIMPI_Retcode MIMPI_Recv(void *data, int count, int source, int tag)
Czeka na wiadomość o wielkości (dokładnie)
count
i znacznikutag
od procesu o randzerank
i zapisuje jej zawartość pod adresemdata
(w gestii wołającego jest zapewnienie odpowiedniej ilości zaalokowanej pamięci). Wywołanie jest blokujące, tzn. kończy się dopiero po otrzymaniu całej wiadomości.Wykonanie
MIMPI_Recv
na rzecz procesu, który nie wysłał pasującej wiadomości i opuścił już blok MPI powinno zakończyć się niepowodzeniem zwracając kod błęduMIMPI_ERROR_REMOTE_FINISHED
. Podobne zachowanie jest oczekiwane nawet w sytuacji gdy drugi proces opuści blok MPI już podczas czekania naMIMPI_Recv
.-
Wersja podstawowa: można zakładać, że każdy z procesów wysyła komunikaty w dokładnie takiej kolejności, w jakiej odbiorca chce je odbierać. Nie można natomiast zakładać, że wiele procesów nie wyśle równocześnie wiadomości do jednego odbiorcy. Można zakładać, że dane powiązane z jedną wiadomością są nie większe niż 512 bajtów.
-
Usprawnienie1: Przesyłane wiadomości mogą być dowolnie (rozsądnie) duże, w szczególności większe niż bufor łącza (
pipe
). -
Usprawnienie2: Nie można zakładać nic o kolejności wysłanych pakietów. Odbiorca powinien być w stanie buforować przychodzące pakiety, a w momencie wywołania
MIMPI_Recv
zwracać pierwszą (ze względu na czas przyjścia) wiadomość pasującą parametramicount
,source
itag
(nie trzeba implementować bardzo wymyślnego mechanizmu wybierania następnego pasującego pakietu; złożoność liniowa względem liczby wszystkich jeszcze nieprzetworzonych przez docelowy proces pakietów jest w zupełności wystarczająca). -
Usprawnienie3: Odbiorca powinien przetwarzać przysyłane wiadomości współbieżnie z wykonywaniem innych czynności, tak by nie doszło do przepełnienia kanałów do wysyłania wiadomości. Innymi słowy wysłanie dużej ilości wiadomości nie jest blokujące nawet jeśli docelowy odbiorca ich nie przetwarza (ponieważ trafiają do wciąż rosnącego bufora).
-
Usprawnienie4: Zakleszczenie to sytuacja, w której część systemu znalazła się w stanie, który nie ma już żadnej możliwości się zmienić (nie istnieje taka sekwencja możliwych przyszłych zachowań procesów, które by to zakleszczenie rozwiązywały). Zakleszczenie pary procesów to sytuacja, w których zakleszczenie jest spowodowane stanem dwóch procesów (rozważając czy można je przerwać dopuszczamy dowolne akcje procesów spoza pary - nawet takie, które nie są dozwolone w ich bieżącym stanie).
Przykłady pewnych sytuacji będącymi zakleszczeniami par procesów w naszym systemie to:
- para procesów wykonała wzajemnie na siebie
MIMPI_Recv
nie wysyłając uprzednio z użyciemMIMPI_Send
wiadomości, która może zakończyć czekanie któregokolwiek z nich - jeden z procesów czeka na wiadomość od procesu, który czeka już na synchronizację związaną z wywołaniem procedury do komunikacji grupowej
W ramach tego usprawnienia należy zaimplementować przynajmniej wykrywanie zakleszczeń par typu 1). Wykrywanie zakleszczeń innych typów nie będzie sprawdzane (można je zaimplementować). Nie należy natomiast zgłaszać zakleszczeń w sytuacjach, które zakleszczeniami nie są.
W przypadku wykrycia zakleszczenia aktywne wywołanie funkcji bibliotecznej
MIMPI_Recv
w obu procesach wykrytego zakleszczenia pary powinno natychmiast się zakończyć zwracając kod błęduMIMPI_ERROR_DEADLOCK_DETECTED
.W przypadku wystąpienia wielu zakleszczonych par jednocześnie należy przerwać wywołanie funkcji bibliotecznej
MIMPI_Recv
w każdym procesie każdej zakleszczonej pary.Detekcja zakleszczeń może do działania wymagać wysyłania wielu pomocniczych komunikatów, co może istotnie spowalniać działanie systemu. Dlatego funkcjonalność tę można włączać i wyłączać na czas działania całego bloku MPI ustawiając odpowiednią wartość flagi
enable_deadlock detection
w wywołaniuMIMPI_Init
rozpoczynającym ten blok.Działanie w przypadku, gdy detekcję zakleszczeń włączono jedynie w niektórych procesach aktualnego wykonania
mimpirun
jest niezdefiniowane.Uwaga: usprawnienie4 (wykrywanie zakleszczeń) wymaga usprawnienia2 (filtrowanie wiadomości). Częściowe wykrywanie zakleszczeń - bez implementacji usprawnienia2 - będzie oceniane na 0 punktów.
- para procesów wykonała wzajemnie na siebie
-
Każda procedura
W przypadku, jeśli synchronizacja wszystkich procesów nie może się zakończyć,
bo któryś z procesów opuścił już blok MPI, wywołanie MIMPI_Barrier
w przynajmniej jednym procesie
powinno zakończyć się kodem błędu MIMPI_ERROR_REMOTE_FINISHED
.
Jeśli proces, w którym się tak stanie w reakcji na błąd sam zakończy działanie,
wywołanie MIMPI_Barrier
powinno zakończyć się w przynajmniej jednym następnym procesie.
Powtarzając powyższe zachowanie, powinniśmy dojść do sytuacji, w której każdy proces
opuścił barierę z błędem.
Każda procedura
-
$n$ to liczba procesów -
$t$ to najdłuższy czas wykonaniachsend
związany z przesyłem jednego komunikatu w ramach danego wywołania funkcji do komunikacji grupowej. Dodatkowe informacje można wyczytać z przykładowej implementacjichannel.c
i z zapewnionych testów z katalogutests/effectiveness
-
$\epsilon$ to nieduża stała (rzędu co najwyżej milisekund), która nie zależy od$t$ -
$w$ to wielkość w bajtach wiadomości przetwarzanej w danym wywołaniu funkcji komunikacji grupowej (w przypadku wywołaniaMIMPI_Barrier
należy założyć$w=1$ )
Dodatkowo aby implementacja została uznana za efektywną przesyłane dane nie powinny być
opatrzone za dużą liczbą metadanych.
W szczególności oczekujemy, że funkcje grupowe wywołane dla danych wielkości
mniejszej niż 256 bajtów będą wywoływać chsend
i chrecv
na rzecz pakietów
wielkości mniejszej lub równej niż 512 bajtów.
Testy z katalogu tests/effectiveness
dołączone w paczce sprawdzają powyżej zdefiniowane pojęcie efektywności.
Przejście ich pozytywnie jest warunkiem koniecznym (choć niekoniecznie wystarczającym)
do zdobycia punktów za efektywną implementację funkcji grupowych.
-
MIMPI_Retcode MIMPI_Barrier()
Przeprowadza synchronizację wszystkich procesów.
-
MIMPI_Retcode MIMPI_Bcast(void *data, int count, int root)
Wysyła dane zapewnione przez proces o randze
root
do wszystkich pozostałych procesów. -
MIMPI_Retcode MIMPI_Reduce(const void *send_data, void *recv_data, int count, MPI_Op op, int root)
Zbiera dane zapewnione przez wszystkie procesy w
send_data
(traktując je jak tablicę liczb typuuint8_t
wielkościcount
) i przeprowadza na elementach o tych samych indeksach z tablicsend_data
wszystkich procesów (równieżroot
) redukcję typuop
. Wynik redukcji, czyli tablica typuuint8_t
wielkościcount
, jest zapisywany pod adresrecv_data
wyłącznie w procesie o randzeroot
(niedozwolony jest zapis pod adresrecv_data
w pozostałych procesach).Dostępne są następujące typy redukcji (wartości
enum
aMIMPI_Op
):MIMPI_MAX
: maksimumMIMPI_MIN
: minimumMIMPI_SUM
: sumaMIMPI_PROD
: produkt
Należy zwrócić uwagę na to, że wszystkie powyższe operacje na dostępnych typach danych są przemienne i łączne i odpowiednio zoptymalizować
MIMPI_Reduce
.
Patrz dokumentacja w kodzie mimpi.h
:
- dokumentacja
MIMPI_Retcode
, - dokumentacja poszczególnych procedur zwracających
MIMPI_Retcode
.
Przyjmujemy konwencję:
-
tag > 0
jest przeznaczony dla użytkowników biblioteki na własne potrzeby, -
tag = 0
oznaczaANY_TAG
. Jego zastosowanie doMIMPI_Recv
powoduje dopasowanie do dowolnego znacznika. Nie należy go używać wMIMPI_Send
(skutek użycia jest niezdefiniowany). -
tag < 0
jest zarezerwowany na potrzeby implementujących bibliotekę i może być zastosowany do wewnętrznej komunikacji.W szczególności oznacza to, że programy użytkownika (np. nasze programy testowe) nigdy nie wywołają bezpośrednio procedury
MIMPI_Send
czyMIMPI_Recv
ze znacznikiem< 0
.
Standard MPI jest zaprojektowany z myślą o obliczeniach uruchamianych na superkomputerach. W związku z tym komunikacja między poszczególnymi procesami zazwyczaj odbywa się przez sieć i jest wolniejsza niż wymiana danych w obrębie jednego komputera.
Aby lepiej zasymulować środowisko działania prawdziwej biblioteki
i tym samym zmierzyć się z jej problemami implementacyjnymi,
komunikację między procesami należy przeprowadzić wyłącznie
z użyciem, dostarczonych w bibliotece channel.h
, kanałów.
Biblioteka channel.h
zapewnia następujące funkcje do obsługi kanałów:
void channels_init()
- inicjalizacja biblioteki kanałówvoid channels_finalize()
- finalizacja biblioteki kanałówint channel(int pipefd[2])
- utworzenie kanałuint chsend(int __fd, const void *__buf, size_t __n)
- wysłanie wiadomościint chrecv(int __fd, void *__buf, size_t __nbytes)
- odebranie wiadomości
channel
, chsend
, chrecv
działają podobnie do pipe
, write
i read
odpowiednio.
Zamysł jest taki, że jedyna istotna (z perspektywy rozwiązania zadania)
różnica w zachowaniu funkcji zapewnionych przez channel.h
jest
taka, że mogą one mieć znacząco dłuższy czas wykonania od swoich oryginałów.
W szczególności zapewnione funkcje:
- posiadają taką samą sygnaturę jak oryginalne funkcje
- podobnie tworzą wpisy w tablicy otwartych plików
- gwarantują atomowość odczytów i zapisów do 512 bajtów włącznie
- gwarantują posiadanie bufora o wielkości przynajmniej 4 KB
- ... (w razie niejasności należy zadać pytanie)
UWAGA:
Należy koniecznie wywołać następujące funkcje pomocnicze: channels_init
z MIMPI_Init
,
zaś channels_finalize
z MIMPI_Finalize
.
Wszystkie odczyty i zapisy na rzecz deskryptorów plików zwróconych przez funkcję channel
należy wykonywać z użyciem chsend
i chrecv
.
Dodatkowo, nie należy wywoływać żadnych funkcji systemowych modyfikujących własności plików jak fcntl
na rzecz deskryptorów plików zwróconych przez funkcję channel
.
Niezastosowanie się do powyższych zaleceń może skutkować całkowitą utratą punktów.
Należy pamiętać, że z powyższych gwarancji dotyczących funkcji chsend
oraz chrecv
nie wynika, że nie przetworzą one mniejszej liczby bajtów niż żądana.
Może się tak stać jeśli wielkość ta przekracza gwarantowany rozmiar bufora kanału,
lub gdy ilość danych w buforze wejściowym jest niewystarczająca.
Implementacje muszą poprawnie obsługiwać taką sytuację.
- Program
mimpirun
ani żadna z funkcji z bibliotekimimpi
nie mogą tworzyć nazwanych plików w systemie plików. - Program
mimpirun
i funkcje z bibliotekimimpi
mogą korzystać z deskryptorów o numerach należących do przedziału$[ 20, 1023 ]$ w dowolny sposób. Dodatkowo można zakładać, że deskryptory z powyższego zakresu nie są zajęte w momencie uruchomienia programumimpirun
. - Program
mimpirun
ani żadna z funkcji z bibliotekimimpi
nie mogą modyfikować istniejących wpisów z tablicy otwartych plików z pozycji spoza$[ 20, 1023 ]$ . - Program
mimpirun
ani żadna z funkcji z bibliotekimimpi
nie mogą wykonywać żadnych operacji na plikach, których same nie otworzyły (w szczególności naSTDIN
,STDOUT
iSTDERR
). - W żadnym miejscu nie można stosować aktywnego ani półaktywnego oczekiwania.
- Tym samym nie należy korzystać z żadnej funkcji usypiającej wykonanie programu
na określony czas (
sleep
,usleep
,nanosleep
) ani też wariantów funkcji z timeout'em (jak np.select
). - Należy czekać wyłącznie na wydarzenia niezależne od czasu np. pojawienie się wiadomości.
- Tym samym nie należy korzystać z żadnej funkcji usypiającej wykonanie programu
na określony czas (
- Rozwiązania będą testowane pod kątem wycieków pamięci i/lub innych zasobów (niezamkniętych plików itd.). Należy uważnie prześledzić i przetestować ścieżki mogące prowadzić do wycieków.
- Można założyć, że odpowiadające i-te wywołania funkcji do komunikacji grupowej
w różnych procesach są tych samych typów (są to te same funkcje) i mają takie same wartości parametrów
count
,root
iop
(jeśli bieżący typ funkcji posiada dany parametr). - W przypadku błędu w funkcji systemowej należy zakończyć wywołujący ją program z niezerowym kodem wyjściowym np.
poprzez użycie dostarczonego makra
ASSERT_SYS_OK
. - W przypadku, gdy programy
$prog$ korzystają z biblioteki w sposób niezgodny z wymienionymi w tej treści gwarancjami, można postąpić dowolnie (nie będziemy takich sytuacji sprawdzać).
- Implementowane funkcje nie muszą być threadsafe, tzn. można założyć, że nie są wołane z wielu wątków równocześnie.
- Implementacje funkcji powinny być rozsądnie efektywne, tzn. niewystawione na ekstremalne obciążenie (np. obsługę setek tysięcy wiadomości) nie powinny dodawać poza oczekiwany czas działania (np. wynikający z oczekiwania na wiadomość) narzutu rzędu dziesiątek milisekund (i większego).
- Wykonanie procedury innej niż
MIMPI_Init
poza blokiem MPI ma niezdefiniowane działanie. - Wielokrotne wywołanie procedury
MIMPI_Init
ma niezdefiniowane działanie. - Dajemy gwarancję, że
channels_init
ustawi obsługę sygnałuSIGPIPE
jako ignorowany. Ułatwi to poradzenie sobie z wymaganiem, byMIMPI_Send
zwróciłMIMPI_ERROR_REMOTE_FINISHED
w stosownej sytuacji.
Wymagamy użycia języka C w wersji gnu11
(samo c11
nie daje bowiem dostępu do wielu przydatnych funkcji z biblioteki standardowej).
Nie pozostawiamy wyboru, bowiem zadanie ma m.in. pogłębić umiejętności posługiwania się językiem C.
Można korzystać ze standardowej biblioteki języka C (libc
), biblioteki pthread
, a także funkcjonalności dostarczanych przez system (zadeklarowanych w unistd.h
itp.).
Niedozwolone jest korzystanie z innych bibliotek zewnętrznych.
Mozna zapożyczać dowolny kod z laboratoriów. Wszelkie inne ewentualne zapożyczenia kodu należy odpowiednio komentować z podaniem źródła.
Paczka zawiera następujące pliki niebędące częścią rozwiązania:
examples/*
: proste przykładowe programy korzystające z bibliotekimimpi
tests/*
: testy sprawdzające różne konfiguracje uruchomień przykładowych programów z użyciemmimpirun
assignment.md
: ten opischannel.h
: plik nagłówkowy deklarujący funkcje do obsługi komunikacji międzyprocesowejchannel.c
: przykładową implementacjęchannel.h
mimpi.h
: plik nagłówkowy deklarujący funkcje bibliotekiMIMPI
Makefile
: przykładowy plik automatyzujący kompilacjęmimpirun
, programów przykładowych i uruchamianie testówself
: pomocniczy skrypt do uruchamiania testów dostarczonych w zadaniutest
: skrypt lokalnie wykonujący wszystkie testy z katalogutests/
test_on_public_repo
: skrypt wykonujący testy według poniżej przedstawionego schematufiles_allowed_for_change
: skrypt listujący pliki, które wolno modyfikowaćtemplate_hash
: plik specyfikujący wersję szablonu, na podstawie którego zostało przygotowane rozwiązanie
Szablony do uzupełnienia:
mimpi.c
: plik zawierający szkielety implementacji funkcji bibliotekiMIMPI
mimpirun.c
: plik zawierający szkielet implementacji programumimpirun
mimpi_common.h
: plik nagłówkowy przeznaczony do deklaracji wspólnych funkcjonalności bibliotekiMIMPI
i programumimpirun
mimpi_common.c
: plik przeznaczony do implementacji wspólnych funkcjonalności bibliotekiMIMPI
i programumimpirun
-
zbudowanie
mimpirun
i wszystkich przykładów z kataloguexamples/
:make
-
uruchomienie lokalnych testów:
./test
-
uruchomienie lokalnych testów z valgrindem :
VALGRIND=1 ./test
-
uruchomienie testów według oficjalnego schematu:
./test_on_public_repo
Powyższa komenda pozwala upewnić się, że rozwiązanie spełnia wymogi techniczne wyszczególnione w schemacie oceniania.
-
wypisanie wszystkich plików otwartych przez procesy uruchomione przez
mimpirun
: np../mimpirun 2 ls -l /proc/self/fd
-
śledzenie błędów pamięci, wycieków pamięci i zasobów:
Przydatne może być narzędzie
valgrind
, w szczególności flagi:--track-origins=yes
--track-fds=yes
Mający węższy zakres działania, ale również pomocny do debugowania może być ASAN (Address Sanitizer). Uruchamia się go podaniem flagi
-fsanitize=address
dogcc
.
Aby ukończyć zadanie należy:
- Uzupełnić szablon rozwiązania według specyfikacji, zmieniając jedynie pliki wymienione w
files_allowed_for_change
(w szczególności należy uzupełnić przynajmniej plikimimpi.c
imimpirun.c
). - Upewnić się, że rozwiązanie pasuje do wymaganego schematu oceniania wywołując
./test_on_public_repo
- Wyeksportować rozwiązanie z użyciem
make assignment.zip && mv assignment.zip ab123456.zip
(zamieniającab123456
na Twój login ze students) i wgrać na czas wynikowe archiwum do moodle.
Jeśli komuś zapewniony szablon się nie podoba i jest bardzo zmotywowany można go zmienić. Najpierw należy skomunikować się z autorami zadania celem wstępnej weryfikacji potrzeby. Po pozytywnej weryfikacji trzeba kolejno:
- Stworzyć fork publicznego repozytorium z rzeczonym szablonem.
- Zaktualizować stworzonego fork'a dodanymi przez siebie poprawkami upewniając się, że nie zawierają one fragmentów rozwiązania
- Otworzyć pull request do głównego repozytorium, opisując zmiany.
- W procesie dyskusji poprawki lub nowy szablon mogą zostać zaakceptowane albo odrzucone.
- Dalej pracować korzystając z nowo-stworzonego szablonu (przełączając się na odpowiedni branch)
Istotne jest aby zaktualizowany szablon pasował do poniższego schematu oceniania,
w szczególności posiadał odpowiednio zaktualizowany plik template_hash
.
Polecenie make
automatycznie próbuje zbudować
wszystkie programy z examples/
do examples_build
.
Dalej wywołanie ./test
uruchamia wszystkie rozpoznawane testy z katalogu tests/
.
Przyjęta jest następująca konwencja:
- pliki
*.self
to specjalny rodzaj testów uruchamiany z użyciem dostarczonego skryptu pomocniczegoself
. W pierwszej linijce specyfikują one polecenie do uruchomienia. Natomiast od 3-ciej linijki do końca pliku znajduje się oczekiwana wartośćSTDOUT
, jaką powinno wygenerować wykonanie powyższego polecenia. - pliki
*.sh
to dowolne skrypty shell'owe. Mogą one wykonywać dowolną logikę. Powinny jedynie zakończyć się kodem$0$ w przypadku sukcesu (przejścia testu) i niezerowym kodem w przypadku wykrycia błedu.
W przypadku przygotowania przez siebie własnych testów pasujących do powyższego schematu zachęcamy do podzielenia się nimi z innymi. W tym celu należy tylko w jakiś sposób przekazać je autorom zadania (najlepiej pull request'em). Aby zachęcić Państwa do tego wysiłku, postaramy się uruchomić tak opublikowane testy na rozwiązaniu wzorcowym i dać informację zwrotną gdyby były niepoprawne.
Wgrane przez Państwa rozwiązania będą budowane i testowane według następującego schematu:
- Paczka
zip
z rozwiązaniem o odpowiedniej nazwieab123456.zip
(podstaw swój login ze students) zostanie pobrana z moodle i rozpakowana. - Zostanie stworzony czysty klon
$K$ publicznego repozytorium zawierającego udostępniony Państwu szablon. - W
$K$ zostanie wybrana gałąź na podstawie wartościtemplate_hash
z Państwa rozwiązania. - Do
$K$ zostaną skopiowane te pliki z Państwa rozwiązania, które są wymienione wfiles_allowed_for_change
z wersji z$K$ - Do
$K$ zostanie skopiowany przygotowany przez nas zestaw testów (odpowiednie pliki wexamples/
itests/
). - W
$K$ zostanie wywołanemake
celem zbudowania Państwa rozwiązania i wszystkich przykładów. - W
$K$ zostanie wywołane./test
- Rozwiązanie zostanie ocenione na podstawie zestawu testów, które przeszło Państwa rozwiązanie.
Wykonanie bez błędów powyższego procesu budowania i przejście następujących automatycznych testów:
- hello
- send_recv
jest warunkiem koniecznym do uzyskania niezerowej liczby punktów.
Aby sprawdzić zgodność swojego rozwiązania z powyższym schematem oceniania należy użyć skryptu ./test_on_public_repo
.
Zachęcamy do skorzystania.
Uwaga: W przypadku rozbieżności w wynikach powyższego procesu brane pod uwagę będzie działanie na serwerze students
.
Uwaga: poniższa punktacja jest wyłącznie orientacyjna i może ulec dowolnym zmianom. Jej celem jest łatwiejsze oszacowanie trudności/złożoności danej funkcjonalności.
- implementacja programu
mimpirun
+ procedurMIMPI_Init
iMIMPI_Finalize
+ przechodzący poprawnie przykład/testhello
(1p) - implementacja funkcji punkt-punkt (przy założeniu wiadomości o wielkości do 500B) (1p)
- implementacja funkcji grupowych (wielkość wiadomości jw.) (1p)
- efektywne (logarytmiczne) funkcje grupowe (2p)
MIMPI_Barrier
(1p)MIMPI_Bcast
orazMIMPI_Reduce
(1p)
- (
MIMPI_Recv
::Usprawnienie1) dowolnie duże wiadomości (dowolnie ponad 500B) (1p) - (
MIMPI_Recv
::Usprawnienie2) obsługa dowolnej kolejności przesyłania wiadomości (filtrowanie po tagach, rozmiarze), w tym obsługa taguMIMPI_ANY_TAG
(1p) - (
MIMPI_Recv
::Usprawnienie3) niezapychalne kanały (1p)- brak górnego ograniczenia na wielkość danych do momentu zablokowania procesu wysyłającego
- brak oczekiwania przy wywołaniu
MIMPI_Send
- (
MIMPI_Recv
::Usprawnienie4) detekcja zakleszczeń par procesów (2p)
Usprawnienia zapisaliśmy w kolejności, w jakiej sugerujemy ich priorytetyzację.