Wydajność programów sekwencyjnych Krzysztof Banaś Obliczenia Wysokiej Wydajności 1
Wydajność obliczeń Dla wielu programów wydajność obliczeń można traktować jako wydajność pobierania z pamięci i przetwarzania przez procesor W rzeczywistości często występują algorytmy (programy), w których wydajność jest ograniczana wyłącznie przez wydajność procesora lub wyłącznie przez wydajność układu pobierania danych z pamięci To, która sytuacja ma miejsce zależy od współczynnika s pm = L o / L a liczby operacji na jedno pobranie danych Odzwierciedleniem tej sytuacji jest tzw. roofline performance model, model wydajności procesora uwzględniający współczynnik s pm W modelu roofline wydajność wyrażana jest w GFLOPS Krzysztof Banaś Obliczenia Wysokiej Wydajności 2
Roofline performance model Dla niskich wartości s pm wydajność wynika z czasu dostępu do pamięci i jest liniową funkcją s pm : W = s pm / t av Dla wysokich wartości s pm wydajność odpowiada wydajności przetwarzania przez procesor W obu przypadkach wydajność może być teoretyczna lub eksperymentalna Położenie punktu załamania dachu jest istotną charakterystyką wydajnościową procesora Arithmetic intensity [FLOPs/Byte] = s pm / rozmiar_danych [FLOPs/access / Bytes/access] Krzysztof Banaś Obliczenia Wysokiej Wydajności 3
Współczynnik s pm dla różnych typów algorytmów W dziedzinie algebry liniowej algorytmami o wysokim s pm są algorytmy tzw. poziomu 3 BLAS (np. mnożenie macierz macierz) Algorytmami o niskim współczynniku s pm są algorytmy poziomu 1 i 2 BLAS (operacje na wektorach i wektorach oraz macierzach, np. iloczyn macierz wektor), zwłaszcza dla macierzy rzadkich Krzysztof Banaś Obliczenia Wysokiej Wydajności 4
Wydajność ograniczana przez procesor Przykład mnożenie macierz macierz: obliczenie iloczynu macierz macierz, rozmiar macierzy nxn, stosunek liczby operacji do optymalnej liczby odniesień, s pm = (2n 3 )/(3n 2 ) ~ 2n/3 wydajność pamięci 8GB/s (109 liczb podwójnej precyzji na sekundę) założenie: każda liczba pobrana z pamięci z taką właśnie szybkością, każda liczba z algorytmu pobrana tylko raz, nie pobierane żadne inne liczby maksymalna wydajność dla optymalnego wykorzystania pamięci 2n/3 GFLOPS dla odpowiednio dużych n wydajność zawsze powinna być ograniczana przez procesor Krzysztof Banaś Obliczenia Wysokiej Wydajności 5
Przykład mnożenia macierzy Wersja 1. for(i=0;i<n;i++){ for(j=0;j<n;j++){ c[i+n*j]=0.0; for(k=0;k<n;k++){ c[i+n*j] += a[i+n*k]*b[k+n*j]; } } } Błędna implementacja może istotnie zmniejszyć współczynnik s pm W przypadku mnożenia macierz macierz z 2n/3 do ~1! Krzysztof Banaś Obliczenia Wysokiej Wydajności 6
Przykład mnożenia macierzy Wersja 3. for(i=0;i<n*n;i++) c[i]=0.0; ib=0; for(j=0;j<n;j++){ for(k=0;k<n;k++){ double t=b[ib]; ic=j*n; ia=k*n; for(i=0;i<n;i++){ c[ic] += t*a[ia]; ic++; ia++; } ib++; } } Krzysztof Banaś Obliczenia Wysokiej Wydajności 7
Przykład mnożenia macierzy Wersja 4.(Możliwe jeszcze register blocking) const int bls=100; for(k=0;k<n;k+=bls){ for(i=0;i<n;i+=bls){ for(j=0;j<n;j++){ register int kk; register int ib=j*n+k; for(kk=k;kk<k+bls;kk++){ register int ii; register double t=b[ib]; register int ic=j*n+i; register int ia=kk*n+i; for(ii=i;ii<i+bls;ii++){ c[ic] += t*a[ia]; ic++; ia++; } ib++; } } } } s pm =? Krzysztof Banaś Obliczenia Wysokiej Wydajności 8
Czasy wykonania w sekundach (n=2000) Wersja _1 Wersja_2 Wersja_3 Wersja_4 MKL PIV, 3GHz, icc O0 (6 Gflops > 2.67 s) 161.66 98.86 64.66 65.28 3.21 PIV, 3GHz, icc O3 (rocznik 2004) 96.70 18.33 18.17 8.96 3.21 Xeon, 3GHz, icc O0 (12 Gflops > 1.33 s) 97.79 67.40 44.44 44.00 1.42 Xeon, 3GHz, icc O3 (rocznik 2008) 52.15 18.39 18.40 6.86 1.42 Xeon, 3GHz, icc O3 wersja równoległa p=8 0.90 (0.62 register blocking) 0.20 Krzysztof Banaś Obliczenia Wysokiej Wydajności 9
Wydajność pamięci Parametrami pamięci wykorzystywanymi w oszacowaniach wydajności mogą być: czas dostępu do pamięci czas pobrania pojedynczej danej (w rzeczywistości pobrane zostaje do pamięci podręcznej więcej danych) teoretyczne z parametrów technicznych praktyczne z pomiarów przepustowość (jako odwrotność czasu dostępu do 1B) teoretyczna (która? wydajność magistrali (chipsetu), pamięci, sterownika pamięci?) praktyczna z testów układu pamięć procesor (zazwyczaj uwzględnia pobieranie z pamięci z optymalnym wykorzystaniem pamięci podręcznej) np. na podstawie testu STREAM: średni czas dostępu [ns] = rozmiar danych [B] / przepustowość [GB/s] Krzysztof Banaś Obliczenia Wysokiej Wydajności 10
Przepustowość pamięci Parametrami podawanymi przez producentów są: wydajność układu pamięci procesora (kiedyś w GB/s, obecnie w GT/s - wymaga znajomości liczby bajtów przesyłanych w pojedynczym takcie (zazwyczaj 8) oraz liczby kanałów pamięci do przeliczenia na GB/s) wydajność chipsetu wydajność pamięci: częstotliwość i szerokość łącza: 1333 GHz, 64B wydajność: np. PC6400 Na podstawie przepustowości można obliczyć średni czas dostępu lub liczbę dostępów na sekundę: W mem [liczba_dostępów/s] = [B/s] / rozmiar_danych [B] Wtedy wydajność programu wynosi: GFLOPS = s pm * W mem Krzysztof Banaś Obliczenia Wysokiej Wydajności 11
Wydajność ograniczana przez pamięć Przykład: obliczenie iloczynu macierz wektor, rozmiar macierzy nxn, stosunek liczby operacji do optymalnej liczby odniesień, s pm = 1 2 wydajność pamięci z testów 8GB/s (10 9 dostępów = liczb podwójnej precyzji na sekundę) założenie: każda liczba pobrana z pamięci z taką właśnie szybkością, każda liczba z algorytmu pobrana tylko raz, nie pobierane żadne inne liczby średni czas dostępu odpowiadający wydajności pamięci 1 ns maksymalna wydajność obliczeń dla optymalnego wykorzystania pamięci 1 2 GFLOPS Krzysztof Banaś Obliczenia Wysokiej Wydajności 12
Wydajność ograniczana przez pamięć Przykład cd.: w algorytmach nieoptymalnych może dojść do niepotrzebnego pobierania danych, ze względu na nieuwzględnienie mechanizmu pamięci podręcznej wersja_2 pobranie każdej liczby powoduje chybienie i przeładowanie pamięci podręcznej (dla linii o rozmiarze 64B oznacza to pobranie jednej potrzebnej i 7 niepotrzebnych liczb, które nie zostaną wykorzystane (tak jakby każdy element macierzy był pobrany 8 razy) zakładając optymalną przepustowość 8GB/s w programie wykorzystywana jest tylko 1/8 mocy średni czas dostępu 8 razy dłuższy 8 ns maksymalna wydajność 0.125GFLOPS w rzeczywistości obsługa chybień w pamięć podręczną jeszcze wydłuża obliczenia i zmniejsza wydajność dalsze kilka razy Krzysztof Banaś Obliczenia Wysokiej Wydajności 13
Przykład Jak wiele czasu może zająć pobranie jednej liczby z pamięci: translacja adresu z TLB, liczba w L1 kilka taktów zegara translacja adresu: miejsce w tablicy stron poza TLB, poza pamięcią RAM na dysku, strona poza pamięcią RAM na dysku, liczba poza pamięcią podręczną L1, poza pamięcią podręczną L2, w pamięci RAM (przed chwilą pobrana z dysku) kilka milionów taktów zegara Różnica w czasie dostępu: kilka rzędów wielkości Krzysztof Banaś Obliczenia Wysokiej Wydajności 14
Wydajność pamięci - uwagi Dla wszystkich poziomów pamięci obowiązuje reguła: linie pamięci podręcznej, liczby banków pamięci RAM są zazwyczaj potęgami 2 aby uniknąć sporów o pamięć (memory contention) poprzez odwzorowanie kolejno pobieranych danych w tę samą linijkę pamięci podręcznej, ten sam bank pamięci itd., należy unikać wymiarów tablic będących potęgami 2 często stosowaną techniką jest padding zmiana wymiaru tablicy tak aby zmienić odstęp w pamięci pomiędzy kolejnymi danymi, do których uzyskuje się dostęp Krzysztof Banaś Obliczenia Wysokiej Wydajności 15
Pamięć wirtualna AK Pamięć wirtualna poszerzenie zakresu adresowania pamięci w stosunku do pamięci głównej (fizycznej) dokonywane przez system operacyjny Pamięć główna zawiera tylko fragment przestrzeni adresowej Pamięć główna dzielona jest na ramki, w których zawarte są strony pamięci wirtualnej Jeśli żądana przez procesor strona nie znajduje się w żadnej z ramek pamięci głównej następuje błąd strony (page fault) i podmiana stron (jak podmiana linii w pamięci podręcznej podobne strategie podmiany) Krzysztof Banaś Obliczenia Wysokiej Wydajności 16
Pamięć wirtualna Procedura dostępu do pamięci obejmuje krok translacji adresów z wirtualnych na fizyczne: procesor generuje adres danych składający się z numeru strony i adresu na stronie sprawdzane jest czy dane strony znajdują się w pamięci TLB jeśli tak (TLB hit) generowany jest adres fizyczny jeśli nie (TLB miss) realizowana jest procedura obsługi chybienia uaktualniana jest pamięć TLB adres fizyczny pobierany jest z tablicy stron w pamięci RAM Krzysztof Banaś Obliczenia Wysokiej Wydajności 17
Pamięć wirtualna Wygenerowany adres fizyczny oznacza numer ramki w pamięci operacyjnej Może zdarzyć się, że ramka została przeniesiona na twardy dysk (błąd strony, page fault) wywoływana jest wtedy systemowa procedura podmiany strony w pamięci RAM (page swapping) koszt obsługi błędu strony sięga wielu milionów taktów procesora błędnie napisane programy mogą powodować nieustanne podmiany stron w pamięci, tzw. szamotanie (thrashing) Krzysztof Banaś Obliczenia Wysokiej Wydajności 18
Pamięć wirtualna Dodatkowe wskazówki optymalizacji dostępu do pamięci: minimalizować liczbę błędów stron minimalizować liczbę chybień w pamięci TLB Dla każdego z wymienionych zdarzeń (łącznie z chybieniami w pamięciach podręcznych różnych poziomów) istnieją w procesorze liczniki zdarzeń sprzętowych (hardware counters), które powinno się odczytywać, po to by zorientować się, do jakiego stopnia optymalny jest dostęp do pamięci w programie Krzysztof Banaś Obliczenia Wysokiej Wydajności 19
Przydatne wzory Jeśli czas wykonania w całości przeznaczony na dostęp do pamięci (obliczenia w tle ): T w = L a x t av t av - obliczane na podstawie czasów dostępu do różnych poziomów hierarchii pamięci powinno uwzględniać sposób dostępu (access pattern) w praktyce uzyskiwane eksperymentalnie (np. poprzez wykonanie programu o zbliżonym sposobie dostępu do pamięci i odpowiednie pomiary) w takim wypadku wydajność obliczana z wzoru W = L o / T w = L o / ( L a x t av ) = s pm / t av s pm = L o / L a - ważny parametr charakteryzujący algorytmy liczba operacji na jedno pobranie danych Krzysztof Banaś Obliczenia Wysokiej Wydajności 20
Wskazówki praktyczne Kolejność postępowania przy optymalizacji sekwencyjnej: wybrać najlepszy algorytm (uwzględnić czy jest podatny na optymalizację wydajności, np. zrównoleglenie) zaimplementować (jeśli nie istnieją zoptymalizowane procedury biblioteczne) zbadać profil czasu wykonania programu (optymalizować ręcznie tylko fragmenty mające istotny wpływ na wydajność całości) eksperymentować z różnymi kombinacjami optymalizacji ręcznej i opcji kompilatora przeglądać generowane pliki w języku asemblera szukając możliwości przyspieszenia działania kodu ewentualnie modyfikować kod asemblera porównywać osiągniętą wydajność z maksymalną teoretyczną wydajnością wykorzystywanego sprzętu Krzysztof Banaś Obliczenia Wysokiej Wydajności 21
Wskazówki praktyczne Optymalizacja ręczna wspomagająca działanie kompilatora: usuwać przeszkody dla optymalizacji automatycznej: zależności danych (zwłaszcza w pętlach) złożone (np. pośrednie) adresowanie tablic w pętlach niestandardowe postacie instrukcji sterujących pętli częste (np. wewnątrz pętli) operacje złożone, jak: wywołania funkcji instrukcje warunkowe operacje wejścia wyjścia zbyt krótkie pętle (lepiej ręcznie rozwinąć) Krzysztof Banaś Obliczenia Wysokiej Wydajności 22
Wskazówki praktyczne Optymalizacja ręczna wspomagająca działanie kompilatora: przeprowadzać optymalizację, która może być zbyt skomplikowana dla kompilatora: reorganizowanie kodu (zmiana kolejności pętli, łączenie, dzielenie) optymalizacja dostępu do pamięci (lokalność odniesień wykorzystanie pamięci podręcznej, prefetching) pamiętać, że ostateczny efekt optymalizacji składa się z: eliminacji zbędnych działań usprawnienia przetwarzania potokowego i ew. wektorowego optymalnego wykorzystania hierarchii pamięci Krzysztof Banaś Obliczenia Wysokiej Wydajności 23
Wskazówki zaawansowanej optymalizacji Uwzględnienie afiniczności pamięci (redukcja dostępów do pamięci odległej, związanej z innym procesorem) Używanie odpowiednio długotrwałych dostępów do kolejnych komórek pamięci (w celu dobrego wykorzystania pobierania z wyprzedzeniem, hardware prefetching, powoduje to także redukcję liczby chybień w TLB) Programowe pobieranie z wyprzedzeniem, software prefetching (o ile uda się uzgodnić ze sprzętowym pobieraniem z wyprzedzeniem) Redukcja konfliktów przy dostępie do pamięci podręcznej poprzez zmianę wymiarowania tablic (padding) zwiększa współczynnik trafień (hit ratio) Rozwijanie i zmiana kolejności pętli w celu uzyskania cache blocking, register blocking oraz umożliwienia wektoryzacji kodu Ręczna wektoryzacja kodu, użycie wstawek z listy rozkazów procesora (intrinsics) Krzysztof Banaś Obliczenia Wysokiej Wydajności 24