materiały pomocnicze do tematów laboratoryjnych
Transkrypt
materiały pomocnicze do tematów laboratoryjnych
Materiały pomocnicze do laboratorium 1. 2. 3. 4. Miary oceny efektywności Mnożenie macierzy Znajdowanie liczb pierwszych Optymalizacja dostępu do pamięci Miary efektywności systemów współbieżnych • System współbieżny - połączenie algorytmu (programu) i architektury równoległej, w której jest on implementowany • Czas przetwarzania (ang. runtime) – Tp – czas przetwarzania równoległego – od momentu rozpoczęcia przetwarzania równoległego do momentu zakończenia przetwarzania przez ostatnią jednostkę przetwarzającą – Ts – czas przetwarzania sekwencyjnego • Koszt zrównoleglenia (ang. parallel overhead) – czas wspólnie spędzony przez jednostki współbieżne nad rozwiązywaniem problemu ponad czas niezbędny do rozwiązania tego samego problemu przez najlepszy algorytm sekwencyjny przy użyciu jednej jednostki przetwarzającej tego samego typu – To = pTp-Ts (liczba procesorów p). • Przyspieszenie – miara zysku wynikającego ze zrównoleglenia przetwarzania (realizowanego na p identycznych jednostkach przetwarzających) w stosunku do sekwencyjnego przetwarzania zrealizowanego przy użyciu najlepszego algorytmu sekwencyjnego – S=Ts/Tp – jednostki przetwarzające systemu równoległego są identyczne do wykorzystywanych w przetwarzaniu sekwencyjnym. 2 Efektywność i koszt • Efektywność określa tę część czasu przetwarzania w jakiej procesory są efektywnie wykorzystane E=S/p • W idealnym systemie równoległym E =1 • Koszt przetwarzania (praca) określa ilość czasu wykorzystywania do rozwiązywania problemu procesorów systemu równoległego C=Tp*p. 3 Mnożenie macierzy – dostęp do pamięci podręcznej [język C, kolejność - j,i,k][1] A[i][*] lokalność przestrzenna danych – rózne A,B,C są tablicami nxn elementy z linii pp wykorzystane w kolejnych for (int j = 0 ; j < n ; j++) iteracjach for ( int i = 0 ; i < n ; i++) B[*][j] brak trafienia do pp, pp ładowana za for (int k = 0 ; k < n ; k++) każdym razem C[i][j] + = A[i][k] * B[k][j] ; C[i][j] lokalność odwołań – ten sam element dla każdej iteracji pętli wewnętrznej = x = C Ilość danych wykorzystywanych w pętli wewnetrznej Ilość danych wykorzystywanych w 2 pętlach wewnętrznych x A B Mnożenie macierzy – pamięć podręczna [C, j,i,k][2] = C x A B C[*][j] brak lokalności przestrzennej odwołań A[*][*] brak lokalności czasowej odwołań B[*][j] brak lokalności przestrzennej, lokalność czasowa odwołań Mnożenie macierzy – pamięć podręczna[C, i,k,j][1] A,B,C są tablicami nxn for ( int i = 0 ; i < n ; i++) for (int k = 0 ; k < n ; k++) for (int j = 0 ; j < n ; j++) C[i][j] + = A[i][k] * B[k][j] ; = x = x A[i][k] – lokalność czasowa odwołań B[k][*], C[i][*] – lokalność przestrzenna odwołań A[i][*] – lokalność przestrzenna odwołań B[*][*] – brak lokalności czasowej odwołań jeżeli suma rozmiaru(wiersz A, wiersz C i tablica B) większe od rozmiaru pamięci podręcznej Mnożenie macierzy – pamięć podręczna [C, i,k,j*] zmniejszenie zakresu pętli wewnętrznej = = x 1X x rx Obliczamy fragment wiersza macierzy wynikowej for ( int j = 0 ; j < n ; j+=r) // cała macierz wynikowa for ( int i = 0 ; i < n ; i++) // wyznaczenie niebieskiej części wyniku for (int k = 0 ; k < n ; k++) // wyznaczenie brązowej części wyniku for (int jj = j ; jj < j+r-1 ; jj++) C[i][jj] + = A[i][k] * B[k][jj] ; Przy odpowiedniej wielkości r możliwa lokalność czasowa odwołań do B[*][jj:jj+r-1] Zmniejszenie wielkości fragmentów tablic, na podstawie których realizowane są obliczenia (w jednej fazie przetwarzania) prowadzi do większej lokalności odwołań (a efektywność DTLB? – rozwiązanie na kolejnym slajdzie). rxr Mnożenie macierzy – pamięć podręczna [operacje na fragmentach tablic][graficznie] C A B for (int kk = k ; kk < k+r ; kk++) C[ii][jj] + = A[ii][kk] * B[kk][jj]; for (int jj = j ; jj < j+r ; jj++) for (int kk = k ; kk < k+r ; kk++) C[ii][jj] + = A[ii][kk] * B[kk][jj]; for ( int ii = i ; ii < i+r; ii++) for (int jj = j ; jj < j+r ; jj++) for (int kk = k ; kk < k+r ; kk++) C[ii][jj] + = A[ii][kk] * B[kk][jj]; for (int k = 0 ; k < n ; k+=r) for ( int ii = i ; ii < i+r; ii++) for (int jj = j ; jj < j+r ; jj++) for (int kk = k ; kk < k+r ; kk++) C[ii][jj] + = A[ii][kk] * B[kk][jj]; Mnożenie macierzy – pamięć podręczna [operacje na fragmentach tablic][kod] #pragma omp parallel for for ( int i = 0 ; i < n ; i+=r) for ( int j = 0 ; j < n ; j+=r) for (int k = 0 ; k < n ; k+=r) // kolejne fragmenty we for ( int ii = i ; ii < i+r; ii++)//fragment wyniku for (int jj = j ; jj < j+r ; jj++) for (int kk = k ; kk < k+r ; kk++) C[ii][jj] + = A[ii][kk] * B[kk][jj]; 1. 3 pętle wewnętrzne służą do wyznaczenia wyniku częściowego dla fragmentu tablicy wynikowej (sum iloczynów elementów wierszy i kolumn fragmentów macierzy wejściowych), 2. czwarta pętla (po k) służy do uzupełnienia wyniku o pozostałe iloczyny wynikające z Dla C[ii][jj], A[ii][kk], B[kk][jj] lokalność czasowa i przestrzenna dostępów przy założeniu, że wszystkie uwzględnienia kolejnych podmacierze A,B i C (A[i:i+r-1][k:k+r-1],B[k:k+r-1][j:j+r(branych po r) elementów 1], C[i:i+r-1][j:j+r-1] ) mieszczą się w pamięci wierszy i kolumn fragmentów podręcznej macierzy wejściowych, Zakładając, że rozmiar pp równy M można wyznaczyć 3. pętle piąta i szósta służą do wymagane r<= (M/3)1/2 wyznaczenia kolejnych kwadratowych (r) obszarów macierzy wynikowej. Znajdowanie liczb pierwszych • Metody: – dzielenie badanej liczby przez liczby pierwsze i badanie wartości reszty z dzielenia – usuwanie wielokrotności - usuwanie ze zbioru badanych liczb liczb będących wielokrotnością liczb pierwszych • Jakie liczby pierwsze uwzględniać dla badanej liczby (bądź górnego zakresu badanego przedziału) n? • Wystarczy znaleźć dla każdej liczby złożonej minimalny podzielnik: dla 35 – 5, dla 77 – 7, dla 121 – 11. • Czy istnieje warunek ograniczający maksymalną wartość najmniejszego podzielnika liczby n? • Tak. • Maksymalna wartość najmniejszego podzielnika liczby złożonej n wynosi n1/2. • Aby znaleźć zatem liczby pierwsze xi<k,l> należy: – usunąć liczby dzielące się bez reszty przez liczby pierwsze brane z przedziału <2, xi1/2> lub – usunąć liczby będące wielokrotnością liczb pierwszych z przedziału <2, l1/2> Sito Eratostenesa koncepcja podejścia funkcjonalnego do podziału w architekturze z przekazywaniem komunikatów – znaczenie koncepcyjne – niska efektywność /2 /3 /5 /7 2,3,4,5,..,120 11,13,17,19,23,29,31,37,41,..., Pierwsza liczba odebrana przez każdy z procesów jest traktowana jako dzielnik i jako liczba pierwsza. Liczby dzielące się z resztą są przesyłane dalej. Wynik przetwarzania – liczby pierwsze pojawiają się na wyjściu systemu oraz rezydują w procesach (należy je przesłać na wyjście). Liczba procesów niezbędbych dla zakresu <n,k> jest równa liczbie liczb pierwszych w zakresie od <2, k1/2> 2k1/2/ln k Wykreślanie z tablicy Z badanego zbioru (tablicy) usuwamy wielokrotności (jakie?) liczb pierwszych z przedziału <2,zakres górny1/2> Przykład dla zakresu : <2,65> 2 : 4,6,8,...64 3 : 9,15,21,27,33,39,45,51,57,63 5 : 25,35,55,65 7 : 49 • nie jest konieczna do rozpoczęcia obliczeń znajomość wszystkich liczb-pierwszych z przedziału <2,zakres górny1/2>; • kolejno pojawiające się liczby pierwsze mogą być wykorzystane dopiero później, gdyż wyznaczanie wielokrotności mniejszych liczb pierwszych odbywa się dla całego badanego przedziału i zajmuje stosunkowo dużo czasu. Zebrane informacje na temat pamięci podręcznej i optymalizacji dostępu do pamięci Porównaj: Ulrich Drepper, What Every Programmer Should Know About Memory Opóźnienie i przepustowość pamięci Opóźnienie = liczba cykli ( lub ns sekund) do pozyskania żądanych danych Przepustowość = Ile danych (np. w MB) można odczytać lub zapisać na sekundę. Opóźnienie zawsze wzrasta z odległością pamięci od procesora dla rodziny Nehalem: 4 cykle L1 cache 10 cykle L2 cache 17 cykle L3 cache 198 (!) cykli RAM Przepustowość nie spada tak mocno z odległością od procesora użytej pamięci, dla pamięci współdzielonej przepustowość jest dzielona na procesory. Krotność skojarzenia pp Krotność skojarzenia (Associativity) - maksymalna liczba niezależnych ciągłych obszarów pamięci, do których dostępy (do pamięci podręcznej) nie wpływają na siebie wzajemnie. W przypadku gdy praca dotyczy większej liczby obszarów danych niż krotność skojarzenia, wtedy pobierane do pamięci podręcznej danych z jednego obszaru powoduje usuwania danych pochodzących z innego obszaru Pamięć podręczna o odwzorowaniu bezpośrednim - krotność skojarzenia =1 W pełni skojarzeniowa pamięć podręczna – krotność skojarzenia = (rozmair pp / rozmiar linii pp) Krotność skojarzenia badanej pp - sprawdzić w dokumentacji używanego procesora Linia PP zapisy Linia pamięci podręcznej: • Kwant rozmiaru PP • Minimalna wielkość danych ładowana z pamięci lub zapisywana do pamięci • 64 bytes we wspólczesnych procesorach x86 CPUs Linia pamięci: zapisy Realizacja zapisu jednego bajtu danych przez program do pamięci 1.64-bajty linii pp ładowane z RAM 2.Zapis jednego bajtu do linii pp Gdy linii zostanie usunięta z pp konieczność zapisania do RAM 64 bajtów linii pp. Zapisy nielokalne przestrzennie - losowo rozrzucone w pamięci – mają wysoki koszt realizacji Linia pp w przetwarzaniu równoległym Tylko jeden rdzeń - właściciel linii pp - może zapisać wartość w tej samej linii pp Realizacja zapisu przez inny rdzeń wymaga: • • • • Usunięcia/unieważnienia linii w pp przez właściciela Zmodyfikowana linia zapisywana jest w pamięci Uzyskanie wykluczającego dostępu do linii pp przez rdzeń zainteresowany zapisem Realizacja zapisu do pp przez rdzeń uprawniony W przypadku współzawodnictwa dostępu zapisu przez 2 rdzenie do tej samej linii mamy niezamierzone współdzielenie - false sharing ( por. zadanie 1 obliczanie Pi) Bufory zapełniania linii pp (Line Fill Buffers) Gdy podczas instrukcji dostępu do pamięci wystąpi brak trafienia do PP L1, dla tego „błędu dostępu” przydzielany jest bufor zapełniania linii ● Gromadzi on fragmenty linii pp dostarczane z dalszych poziomów pamięci ● Liczba buforów ogranicza liczbę równocześnie obsługiwanych przez procesor braków trafienia do pp, L1 procesory Nehalem mają 10 buforów. Wyprzedzające pobieranie danych do pp i wyprzedzające ładowanie danych (dynamiczne wykonywanie instrukcji) Można spowodować wyprzedzające pojawianie się braku trafienia, aby zagwarantować obecność linii pp w momencie gdy będzie potrzebna. Sposoby realizacji: ● ● – ● – Użycie specjalnych instrukcji wyprzedzającego pobrania mogą nie być respektowane przez procesor Ładowanie jednego elementu linii pp z wyprzedzeniem może zablokować potok przetwarzania Oczekiwanie na samodzielne wykrycie wcześniej potrzeby sprowadzenia danych do pp (układy analizy dostępów) może nie działać Przykład 4 suma wektora z częściowym rozwinięciem pętli - dla pomocy w równoległej realizacji pętli double suma_wektora (const double *data, size_t length) { double sum0 = 0.0, sum1 = 0.0; double sum2 = 0.0, sum3 = 0.0; for (; length >= 4; length -= 4) { sum0 += data[0]; sum1 += data[1]; sum2 += data[2]; sum3 += data[3]; data += 4; } … sumowanie ostatnich co najwyżej 3 elementów... return sum0 + sum1 + sum2 + sum3; } Przykład 5: Wyprzedzające czytanie danych double suma_wektora(const double *data, size_t length) { double sum0 = 0.0, sum1 = 0.0; double sum2 = 0.0, sum3 = 0.0; N=wielkość_wyprzedzenia; //dla typu double N = 2 wyprzedzenie wynosi długość linii pp // 4*2*8bajtów(double)= 64 //każde kolejne wyprzedzające czytanie dotyczy kolejnego obszaru //oddalonego o 1/2 linii pp … sumowanie początkowych elementów... for (; length > 4 * N; length -= 4) { sum0 += data[4 * N]; sum1 += data[1]; sum2 += data[2]; sum3 += data[3]; data += 4; } ... sumowanie ostatnich elementów ... return sum0 + sum1 + sum2 + sum3; } Przykład 6: Wyprzedzające pobieranie danych do pp double suma_wektora (const double *data, size_t length) { double sum0 = 0.0, sum1 = 0.0; double sum2 = 0.0, sum3 = 0.0; for (; length >= 4; length -= 4) { _mm_prefetch((char*)&data[4 * N], _MM_HINT_T0); sum0 += data[0]; sum1 += data[1]; sum2 += data[2]; sum3 += data[3]; data += 4; } ... process last elements ... return sum0 + sum1 + sum2 + sum3; } Wyprzedzające pobranie void _mm_prefetch(char* address, int hint); ● Parametr: address okresla adres pamięci z którego wartość jest ładowana do pp, ładowana jest cała linia ● Parametr: hint określa poziom pamięci o _MM_HINT_T0, _MM_HINT_T1, _MM_HINT_T2 odpowiadają odpowiednio poziomom L1, l2 i L3 Stronicowanie Pamięć jest podzielona na strony: Strona jest jednostką pamięci na poziomie systemu operacyjnego ● Nie można przydzielić do procesu mniej niż jedną stronę o Atrybuty pamięci ustalane są dla strony: czytanie zapis, wykonanie o Strona pamięci jest odwzorowana na pamięć fizyczną o W systemach x86 domyślny rozmiar strony pamięci to 4 kbajty. Strona opisana jest przez deskryptor, który przechowuje informacje o: Adresie fizycznym strony w pamięci operacyjnej Atrybutach strony Brak deskryptora świadczy, że pamięć fizyczna nie została stronie przydzielona Gdy w kodzie znajduje się odwołanie do pamięci procesor odwołuje się do deskryptora, aby zrealizować dostęp do strony. W przypadku braku deskryptora wywoływane jest przerwanie sprzętowe. Gdy deskryptor o właściwym typie dostępu istnieje dostęp może być zrealizowany – adres fizyczny jest określony przez deskryptor strony. Bufor translacji adresu TLB - Translation Lookaside Buffer - bufor translacji adresu ● Pamięć podręczna dla deskryptorów stron ● Wielopoziomowa oddzielna dla deskryptorów kodu i danych ● Wielkość zależna od procesora ● Brak trafienia do bufora translacji powoduje poszukiwanie deskryptora w systemie pamięci, deskryptory są przechowywane w pp. Omijanie pamięci podręcznej przy zapisie Zapisy typowo realizowane są do pp, realizacja: ładowanie linii pp do pp, zapis wszystkich 64 bajtów – nie tylko tych zmienionych, operacja zapisu powoduje wzrost wymagań na przepustowość - transfer danych w 2 kierunkach. Specjalne instrukcje wprowadzone w ramach SSE dla zapisów omijających pp. –_mm_stream_si128 odpowiada _mm_store_si128 –_mm_stream_pd odpowiada _mm_store_pd –_mm_stream_ps odpowiada _mm_store_ps Wymagają danych wyrównanych do granicy linii pp. _mm_stream_xx intrinsics zapisują do specjalnych buforów łączonych zapisów Zapełnione bufory (64 bajtami) są bezpośrednio zapisywane w pamięci operacyjnej. _mm_sfence intrinsic powoduje zapis buforów