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