pliki prefetch

Transkrypt

pliki prefetch
Optymalizacja wykonania programów sekwencyjnych
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
1
Optymalizacja sekwencyjna
• Przez optymalizację rozumie się zmiany dokonywane w kodzie źródłowym lub w trakcie tłumaczenia kodu źródłowego na język maszynowy mające na celu osiągnięcie pożądanych cech przez program wynikowy
• Optymalizacji kodu dokonuje się zazwyczaj ze względu na jeden z dwóch czynników:
– rozmiar kodu
– szybkość działania kodu (wydajność)
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
2
Optymalizacja sekwencyjna
• Efekt optymalizacji wydajności programów uzyskuje się najczęściej poprzez:
– redukcję liczby wykonywanych operacji
– optymalizację dostępu do pamięci
– umożliwienie sprawniejszego przetwarzania potokowego przez procesor
• Ten ostatni cel może być uzyskiwany np. w efekcie usunięcia pojawiających się w programach zależności danych
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
3
Optymalizacja sekwencyjna
• Optymalizację przeprowadzić można:
– “ręcznie” stosując odpowiednie techniki
– wykorzystując opcje optymalizującego kompilatora
– przekazując, jeśli jest taka możliwość, wykonanie części kodu procedurom zoptymalizowanych bibliotek
• Opłacalność wyboru jednego z powyższych sposobów zmienia się w czasie i zależy od szeregu czynników, takich jak np.:
– istnienie i jakość zoptymalizowanych bibliotek
– wiek i typowość sprzętu, na którym dokonywane są obliczenia
– typowość optymalizowanego programu
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
4
Optymalizacja sekwencyjna
• Klasyczne sposoby optymalizacji obejmują szereg technik, które można stosować ręcznie i które realizowane są także przez optymalizujące kompilatory
• Zyski ze stosowania poszczególnych technik zależą i od szczegółowej struktury kodu, i od własności procesora, na którym przeprowadzane są obliczenia
• W korzystnych przypadkach optymalizacja sekwencyjna może prowadzić do kilkudziesięciokrotnego zmniejszenia czasu wykonania programów
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
5
Klasyczne techniki optymalizacji
• Optymalizacja dotycząca zmiennych i wyrażeń:
–
–
–
–
–
constant folding (zwijanie stałych)
copy propagation (propagacja kopii)
strength reduction (redukcja złożoności wyrażeń)
variable renaming (przemianowanie zmiennych)
common subexpression elimination (eliminacja powtarzających się podwyrażeń)
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
6
Klasyczne techniki optymalizacji
• Optymalizacja wykonania pętli:
– induction variable simplification (uproszczenie wyrażeń zawierających indeks pętli)
– loop invariant code motion (usunięcie poza pętle kodu niezależnego od iteracji)
– loop interchange (zamiana kolejności wykonywania pętli)
– loop fusion (łączenie pętli)
– loop fission (rozdzielanie pętli)
– loop unrolling (rozwijanie pętli)
– blocking (grupowanie instrukcji ze względu na dostęp do pamięci podręcznej)
• Przykład: mat_vec
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
7
Klasyczne techniki optymalizacji
• Optymalizacja na poziomie instrukcji:
– dead code removal (usuwanie nieosiągalnego lub produkującego zbędne dane kodu)
– tail­recursion elimination (eliminacja rekursji ogonowej)
– inlining (wplatanie procedur – rozwijanie w miejscu wywołania)
– software prefetching – pobieranie z wyprzedzeniem realizowane programowo
– software pipelining – przetwarzanie potokowe na poziomie kodu źródłowego
– i wiele innych
• Przykład: ddot
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
8
Kompilatory optymalizujące
➔
➔
Optymalizacja w trakcie kompilacji odbywa się najczęściej po analizie składniowej, przed generowaniem kodu pośredniego (object code)
Kompilator operuje na formie kodu przetworzonej przez analizator składni do jednej z możliwych postaci, takich jak: drzewa, kod dwuadresowy, kod trójadresowy, odwrotna notacja polska itp.
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
9
Przykład postaci pośredniej
while( j < n ) {
k = k + 2j;
m = 2j;
j++;
}
Krzysztof Banaś
A: t1 := j;
t2 := n
t3 := t1 < t2
jmp (B) t3
jmp (C)
B: t4 := k
t5 := j
t6 := t5 * 2
t7 := t4 + t6
k := t7
t8 := j
t9 := t8 * 2
m := t9
....
jmp (A)
C: ...
Obliczenia Wysokiej Wydajności
10
Kompilatory optymalizujące
➔
Analiza składniowa umożliwia nadanie programowi struktury, w której uwzględnia się:
– przepływ sterowania: możliwości przekazania sterowania z jednego punktu kodu do drugiego
– przepływ danych: określenie miejsca, w którym nadaje się wartość zmiennej i miejsc, w których się z tej wartości korzysta
➔
Analiza prowadzi do zapisu programu z użyciem:
– rejestrów
– bloków podstawowych
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
11
Kompilatory optymalizujące
• Blok podstawowy:
– sekwencja instrukcji charakteryzująca się tym, że jeżeli wykonywana jest jedna z nich wykonywane są wszystkie
– z wnętrza bloku podstawowego nie można wyskoczyć (instrukcja skoku jest zawsze końcem bloku)
– do wnętrza bloku nie można wskoczyć (dowolna instrukcja opatrzona etykietą lub będąca celem skoku jest początkiem bloku podstawowego
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
12
Przykłady kodu asemblera
.L2
.L4
movl ­4(%ebp), %eax cmpl ­12(%ebp), %eax jl .L4
jmp .L3
// j ­> eax // n <> eax ?
movl ­4(%ebp), %eax
movl %eax, %edx
leal 0(,%edx,2), %eax
addl %eax, ­8(%ebp)
movl ­4(%ebp), %eax
movl %eax, %edx
leal 0(,%edx,2), %eax
movl %eax, ­16(%ebp)
incl ­4(%ebp)
jmp .L2
// j­> eax
// j­>edx
// eax=2*edx // k += eax (k+=2*j)
// j­>eax
// j­>edx
// eax=2*edx
// m=eax (m=2*j)
// j++
.L3
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
13
Przykłady kodu asemblera
.L2
.L4
.L4
movl ­4(%ebp), %eax cmpl ­12(%ebp), %eax jl .L4
jmp .L3
// j ­> eax
// n <> eax ?
movl ­4(%ebp), %eax
movl %eax, %edx
leal 0(,%edx,2), %eax
addl %eax, ­8(%ebp)
movl ­4(%ebp), %eax
movl %eax, %edx
leal 0(,%edx,2), %eax
movl %eax, ­16(%ebp)
incl ­4(%ebp)
jmp .L2
// j­> eax
// j­>edx
// eax=2*edx
wersja 2 (IVS):
// k += eax
// j­>eax
.L4:
// j­>edx
addl
$1, %ecx
// eax=2*edx
addl
%eax, %edx
// m=eax
// j++
addl
$2, %eax
cmpl
%r8d, %ecx
jne .L4
.L3
Krzysztof Banaś
leal (%edx, %eax, 2), %edx
leal 0(,%eax,2), %ecx
incl %eax
cmpl %ebx, %eax
jl .L4 Obliczenia Wysokiej Wydajności
// edx+=2*eax
// ecx=2*eax
// eax+=1
// n<>eax ?
// j++
// k+=m
// m+=2
// n<>j ?
14
Kompilatory optymalizujące
• Typowe opcje optymalizacji:
– poziomy optymalizacji (grupowanie różnych technik ze względu na czas działania i agresywność – ingerencję w pierwotną strukturę kodu): zazwyczaj oznaczane O0,O1,itd. (najwyższe opcje oznaczają często zrównoleglenie kodu)
– szczegółowe techniki optymalizacji (przykłady z gcc):
• ­fstrength­reduce, ­fcse­follow­jumps, ­ffast­math, ­funroll­loops, ­fschedule­insns, ­finline­functions, ­fomit­frame­pointer
• niektóre dostępne dla ręcznej optymalizacji, inne nie
– wektoryzacja (wykorzystanie rozkazów SIMD)
– zrównoleglenie (model z pamięcią wspólną, najczęściej poprzez uzupełnienie kodu dyrektywami OpenMP)
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
15
Język asemblera ­ AK
• Przykład: lista rozkazów IA­32
– transfer danych: • mov: przesunięcie (bez operacji pamięć­pamięć)
• push, pop: operacje na stosie; ld, st – load, store
– operacje arytmetyczne i logiczne:
• add, sub, mul: standardowe +, ­, *
• inc, dec, neg: ++1, ­1, *=(­1)
• xor, and, or: działania logiczne (na bitach)
– leal: obliczenie adresu (bez transferu danych)
– cmp: obliczenie wyrażenia warunkowego
– przeniesienie sterowania
• jmp, jge, je, jl: skok bezwarunkowy i warunkowe
• call, ret: obsługa wywołań procedur
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
16
Język asemblera ­ AK
• Przykład: lista rozkazów IA­32
– argumenty:
• bezpośrednie
• zawartość rejestrów
• zawartość komórek pamięci o obliczonym adresie
– obliczanie adresu:
• adres = base + index*scale + disp
• notacja AT&T: disp(base,index,scale)
• base i index są zawartościami rejestrów 32­bitowych
• disp i scale są liczbami (scale=1,2,4,8)
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
17
Język asemblera ­ AK
➔
Przykład: lista rozkazów IA­32

dostępne rejestry
 32­bitowe: %eax (akumulator), %ebx, %ecx, ..., %ebp (wskaźnik ramki), %esp (wskaźnik stosu)
 16­bitowe: %ax (adresowanie połówki %eax), itd.
 8­bitowe: %ah, %al (adresowanie połówek %ax), itd.
 segmentowe: %cs (kod), %ds (dane), %ss (stos), itd.
 kontrolne, uruchamiania (debugowania), testowe
 zmiennoprzecinkowe w postaci stosu: %st(0), %st(1), itd.
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
18
Lista rozkazów x86_64 ­ AK
➔
Powszechne obecnie rdzenie 64­bitowe

➔
Szereg optymalizacji ze względu na wydajność




➔
Standardowe rejestry 64­bitowe
Większa liczba rejestrów (16: %rax ­ %r15 )
Argumenty (do 6 całkowitych i do 8 zmiennoprzecinkowych) procedur przekazywane poprzez rejestry
Nowe rozkazy operacji całkowitych (końcówka q dla argumentów 64­bitowych) i zmiennoprzecinkowych (końcówka sd)
Rozbudowana liczba rozkazów wektorowych (operujących na 16 rejestrach 128 bitowych, %xmm00 – %xmm15 )
Wzrost stopnia komplikacji kodu asemblera

Dużo rozkazów manipulacji zmiennymi (np. cltq a.k.a. cdqe zmiana liczby z 32 na 64­bitową, inne zmieniające rozmiar danych)
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
19
Język asemblera ­ AK
• Pliki w języku assemblera:
– dwie notacje składni: firmy Intel i firmy AT&T
– składnia AT&T (przyjęta w asemblerze GNU):
• nazwa rozkazu zawiera jako ostatnią literę typ argumentu
• źródło danych poprzedza cel wyniku
• argumenty bezpośrednie poprzedzone są symbolem $
• nazwy rejestrów poprzedzone są znakiem %
• osobne rozkazy procesora i koprocesora liczb zmiennopozycyjnych (te ostatnie zaczynają się literą f)
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
20
Asembler GNU ­ AK
• Pliki asemblera GNU (standardowy asembler Linuxa)
– pojedyncza linia pojedynczą instrukcją
– instrukcja
• etykieta:
• .dyrektywa_asemblera
• rozkaz_asemblera
– dyrektywy określają: pliki (.file), sekcje (segmenty) (.section), stałe (napisy, liczby, znaki) (“...”, .ascii, .float,...), wyrównania (.align), symbole wspólne (.comm), itp.
– rozkazy_asemblera odpowiadają rozkazom procesora
Krzysztof Banaś
Obliczenia Wysokiej Wydajności
21

Podobne dokumenty