1 / 113

Vývoj vysoce výkonného software

Vývoj vysoce výkonného software. O čem to bude. O čem to bude. Maximální využití výkonu procesoru a paměti Moderní programování vs. výkon Relevantní vlastnosti moderních procesorů ILP, SIMD Nástroje pro ladění výkon u Co dokáže a co nedokáže překladač Paměťová hierarchie

kalkin
Download Presentation

Vývoj vysoce výkonného software

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Vývoj vysoce výkonného software NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  2. O čem to bude NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  3. O čem to bude NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek • Maximální využití výkonu procesoru a paměti • Moderní programování vs. výkon • Relevantní vlastnosti moderních procesorů • ILP, SIMD • Nástroje pro ladění výkonu • Co dokáže a co nedokáže překladač • Paměťová hierarchie • Cache-Aware a Cache-Oblivious algoritmy

  4. O čem to nebude NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  5. O čem to nebude NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek • Programování v asembleru • Překladače jsou většinou lepší než lidé • Ale naučíme se využívat SIMD instrukce z C++ • Paralelní programování • Viz NPRG042 Programování v paralelním prostředí • Poučení z minulých let: Pečlivá implementace jednovláknové verze přináší stejné zrychlení jako paralelizace • Optimalizace programů překladačem • Viz NSWI109 Konstrukce překladačů • Ale dozvíte se, co můžete od překladače očekávat

  6. Proč? NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  7. Loňské DÚ 1 (letošní DÚ 2 bez optimalizace) – výsledky studentů NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  8. Loňské DÚ2 (letošní DÚ2 včetně optimalizace) - výsledky studentů NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  9. Motivační příklad NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  10. Motivační příklad - DÚ 0+1 NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek • Hromada jablek a pomerančů, spočítejte zrníčka • class Apple ... • { ... • ... int seeds() { return ...; } • }; • class Orange ... • { ... • ... int seeds() { return ...; } • }; • vector< Apple + Orange> data;

  11. Motivační příklad • class Fruit { • ... • virtual int seeds() = 0; • }; • class Apple: public class Fruit { • virtual int seeds() { return ...; } • ... • }; • class Orange : public class Fruit { • virtual int seeds() { return ...; } • ... • }; • vector< Fruit *> data; • Klasické objektové řešení • Abstraktní třída s virtuální funkcí • Konkrétní třídy • Různá data • Různé implementace virtuální funkce • Kontejner • Obsahuje ukazatele na abstraktní třídu • Toto řešení existuje ve všech objektových jazycích • V jazyzích s referenční semantikou jsou ukazatele skryté • List< Fruit> data; NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  12. Motivační příklad • class Fruit { • virtual int seeds() = 0; • }; • class Apple: public class Fruit { • virtual int seeds() { return d2; } • int d1, d2, d3; • }; • class Orange : public class Fruit { • virtual int seeds() { return d3; } • int d1, d2, d3, d4, d5; • }; • vector< Fruit *> data; • int s = 0; • for_each( data.begin(), data.end(), • [&]( Fruit * p) { s += p->seeds(); }); • Jak je to rychlé? • Testovací data • 5 konkrétních tříd lišících se počtem datových položek • Implementace virtuální funkce čtou některou z položek • Kontejner naplněn náhodnou směsí konkrétních tříd NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  13. Motivační příklad • void test() • { • int s = 0; • for_each( data.begin(), data.end(), • [&]( Fruit * p) { s += p->seeds(); }); • } • generate_data(); • test(); // cold run • time_before = now(); • for ( inti = 0; i < N; ++ i) • test(); • time_after = now(); • cout << (time_after - time_before) / N; • Testovací prostředí • Test je opakován N-krát • N je voleno tak, aby celkový čas byl přiměřený • V našem testu sekundy • Program si měří čas sám • Vestandardu C++ vhodná funkce now() není • Plaformy nabízejí různé možnosti • Ty lepší jsou pouze pro privilegované • V našem testu měříme "wall clock" • Je to to, co uživatele zajímá? • Zatíženo paralelně běžícími procesy • Granularita 1-10 ms NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  14. Výsledky • Výsledky testu • 1 000 000 objektů • Celkem 10,4 ms • 10,4 ns na objekt • 1 000 objektů • Celkem 9,0 µs • 9,0 ns na objekt • Spolehlivost výsledků? • Při opakování se výsledky lišío5-10% • Pro orientacidostatečné • Statisticky správné měření je věda • NSWI131 Vyhodnocování výkonnosti počítačových systémů • Hardware • Intel Core2Quad Q6700 • 2,66 GHz • 4 GB RAM • Operační systém • Windows 7 64-bit • 64-bitový kód • Překladače • MS Visual C++ 2010 • MS Visual C++ 2012 • Intel Composer XE 2013 • Rozdíly menší než 5% NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  15. Výsledky - porovnání architektur na témže HW • 64-bitový kód (Intel-64) • 1 000 000 objektů • Celkem 10,4 ms • 10,4 ns na objekt • 1 000 objektů • Celkem 9,0 µs • 9,0 ns na objekt • Ukazatele jsou delší • Procesor vykonává více práce • Přesto běží rychleji! • Architektura ma víc registrů • Procesor je optimalizován pro tento mód • Nebo je to jinak... • 32-bitový kód (IA-32) • 1 000 000 objektů • Celkem 10,7 ms • 10,7 ns na objekt • 1 000 objektů • Celkem 10,0 µs • 10,0 ns na objekt NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  16. Výsledky - závislost na distribuci dat • 64-bitový kód (Intel-64) • 1 000 000 objektů náhodně • 10,4 ns na objekt • 1 000 objektů náhodně • 9,0 ns na objekt • 1 000 000 objektů round-robin • 8,0 ns na objekt • 1 000 objektů round-robin • 2,6 ns na objekt • 1 000 000 objektů skupinově • 7,6 ns na objekt • 1 000 objektů skupinově • 2,8 ns na objekt • 32-bitový kód (IA-32) • 1 000 000 objektů náhodně • 10,7 ns na objekt • 1 000 objektů náhodně • 10,0 ns na objekt • 1 000 000 objektů round-robin • 5,9 ns na objekt • 1 000 objektů round-robin • 2,2 ns na objekt • 1 000 000 objektů skupinově • 5,3 ns na objekt • 1 000 objektů skupinově • 2,6 ns na objekt NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  17. Příčiny NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek • Predikce skoků • Volání virtuální funkce je nepřímý skok • Dokud není znám cíl skoku, dekódování instrukcí nemůže pokračovat • Procesory předvídají cíle • Z předchozích průchodů tímtéž kódem • Asociativní paměť adresa skokové instrukce - adresa cíle • Heuristické metody predikce • Call-return páry • Když predikce nevyjde • Dekódované a částečně provedené instrukce se zahazují • Čeká se na dekódování těch správných • Zdržení v řádu jednotek až desítek cyklů procesoru

  18. Příčiny NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek • Predikce skoků • Volání virtuální funkce je nepřímý skok • Dokud není znám cíl skoku, dekódování instrukcí nemůže pokračovat • Procesory předvídají cíle • Z předchozích průchodů tímtéž kódem • Asociativní paměť adresa skokové instrukce - adresa cíle • Heuristické metody predikce • Call-return páry • Když predikce nevyjde • Dekódované a částečně provedené instrukce se zahazují • Čeká se na dekódování těch správných • Zdržení v řádu jednotek až desítek cyklů procesoru

  19. Typické mikroarchitektury procesorů (2000-2012) NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  20. NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  21. Intel Netburst Microarchitecture [2000] NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  22. Intel NetBurst Microarchitecture [2000] NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  23. Intel Core Microarchitecture Pipeline [2006] NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  24. Intel Core Microarchitecture NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek • Procesor (teoreticky) zvládá v jediném cyklu najednou: • Fetch: 16 B (cca. 4 instrukce) z instrukční cache • Decode: 1 až 5 instrukcí • ALU: 3 jednodušší operace (add/mul) • Memory load: 1 čtení (až 128 bitů) z L1 cache • Memory store: 1 zápis (až 128 bitů) z L1 cache • Doba trvání operací (latence) • integer add, mul: 1 • FP add: 3, FP mul: 4-5 • div: podle dat • integer load: 3, FP load: 4 (L1 cache) • store address: 3 • store data: 2 (retirement, in-order)

  25. Intel Core Microarchitecture NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek • Branch prediction • podmínky, nepřímé skoky, call/return páry • spekulativní provádění • Dekodér instrukcí • loop cache (jednoduché cykly do 18 instrukcí) • převod na mikrooperace 1:1, 1:N, 2:1 • simulátor ukazatele zásobníku • Renamer • 16 logických registrů mapováno do 144 fyzických (podobně FP registry) • Out-of-order execution • 32 rozpracovaných mikrooperací (RS) z okna délky 96 (ROB) • retirement: zápisy do paměti/registrů běží in-order opožděně na pozadí • store forwarding: čekající čtení dostávají hodnotu přímo ze zápisu • spekulativní čtení: nečeká se na předchozí store

  26. Intel Nehalem Pipeline [2008] NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  27. Intel Sandy Bridge Pipeline [2011] NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  28. Intel vs. AMD architectures (realworldtech.com) NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  29. Intel Haswell Microarchitecture NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  30. Pohled překladače na procesor NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  31. NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek • Scheduling - volba uspořádání instrukcí • Nejpodstatnější fáze překladače z hlediska výkonu kódu • Platilo do nedávna, narušeno out-of-order execution v CPU • Hledá se takové pořadí které je • Ekvivalentní z hlediska efektu/pravidel jazyka • Vyhovuje dependencím • Nejrychlejší • Model časování procesoru

  32. Závislosti Závislost (dependence) Povinnost provést jednu operaci/instrukci po jiné Částečné uspořádání operací/instrukcí v jednom BB Datová závislost (dependence) Závislost producent-konzument v toku dat Antidependence Read-Write: Čtení se musí stihnout před zápisem Write-Write: Pořadí zápisů se nesmí změnit Jiné důvody, obvykle nízkoúrovňového původu

  33. Scheduling Scheduling pouze odhaduje skutečné časování Skutečné časování je ovlivněno nepředvídatelnými jevy Zbytky rozpracovaných instrukcí z předchozího kódu Řešení: Trace-scheduling, řízení profilem Paměťová hierarchie Doba přístupu k paměti závisí na přítomnosti v cache Obvykle se předpokládá nejlepší možný průběh Speciální problém: Multithreaded aplikace na multiprocesorech Fetch bandwidth Instrukce nemusí být načteny a dekódovány včas Zdržují skoky a soupeření o přístup do paměti Přesné simulování fetch jednotky by neúměrně komplikovalo scheduler Scheduler nezná skutečné závislosti přístupů do paměti Musí postupovat opatrně a zohledňuje i nejisté závislosti Procesor zná skutečné adresy přístupů a detekuje pouze skutečné závislosti Agresivně optimalizující procesor může zvolit zcela jiné pořadí instrukcí

  34. Scheduling Model procesoru Latence – časování závislých dvojic instrukcí Počet cyklů procesoru, který musí proběhnout mezi referenčními body závislých instrukcí U antidependencí a ve speciálních případech může být nulová U procesorů se sekvenčními body může být záporná

  35. Scheduling Model procesoru Rezervační tabulky – schopnosti paralelního zpracování Procesor je rozdělen na funkční jednotky různých druhů Je určen počet jednotek každého druhu Pro každou instrukci definována rezervační tabulka Počet jednotek daného druhu, který instrukce potřebuje v daném čase (měřeno od referenčního bodu) Rezervační tabulky jsou nutné i pro procesory, které nejsou super-skalární

  36. Scheduling - pokročilé techniky Loop unrolling V cyklech s malým počtem instrukcí někdy není co paralelizovat Současné zpracování dvou nebo více iterací pomůže Modulo scheduling, software pipelining

  37. Příklad – Intel compiler – x64 /*...*/ k = i >> 1; j = 0; do { r8 = *p; r9 = *(p+1); s ^= r8; s ^= r9; p += 2; j += 1; } while ( j < k ); /* ... */ charchksum( char* p, inti) { char s = 0; while ( i > 0 ) { s ^= *p++; --i; } return s; } ..B1.4: movsbq (%rdi), %r8 movsbq 1(%rdi), %r9 xorl %r8d, %eax xorl %r9d, %eax addq $2, %rdi addl $1, %ecx cmpl %edx, %ecx jb ..B1.4

  38. Průchod polymorfní datové struktury - pohled procesoru NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  39. Kritický kód - Intel C++ 64-bit • Zdrojový kód • std::for_each( b, e, [&](Fruit * p){ s += p->seeds(); }); • Smyčka po inline expanzi • for(; b != e; ++ b) s += (*b)->seeds(); • Generovaný strojový kód • .lp: • mov rcx, QWORD PTR [r14] • mov rax, QWORD PTR [rcx] • call QWORD PTR [8+rax] • add r14, 8 • add DWORD PTR [88+rbp], eax • cmp r14, r13 • jne .lp ; Prob 82% • Tělo virtuální funkce seeds • mov eax, DWORD PTR [8+rcx] • ret • Registry • r14 = b • r13 = e • rcx = * b • rax = VTable • eax = hodnota f() • rbp = stackframe • [88+rbp] = s • Skoky • dobrá predikce • ret • jne • špatná predikce • call NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  40. Kritický kód - Intel C++ 64-bit • Zdrojový kód • std::for_each( b, e, [&](Fruit * p){ s += p->seeds(); }); • Smyčka po inline expanzi • for(; b != e; ++ b) s += (*b)->seeds(); • Generovaný strojový kód • .lp: • mov rcx, QWORD PTR [r14] • mov rax, QWORD PTR [rcx] • call QWORD PTR [8+rax] • add r14, 8 • add DWORD PTR [88+rbp], eax • cmp r14, r13 • jne .lp ; Prob 82% • Tělo virtuální funkce seeds • mov eax, DWORD PTR [8+rcx] • ret • Závislosti • write-read • read-write • test-access • Restart pipeline • Špatně predikovaný skok do virtuální funkce NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  41. Kritický kód - Intel C++ 64-bit • Tělo virtuální funkce seeds • mov eax, DWORD PTR [8+rcx] • ret • Konec smyčky • add r14, 8 • add DWORD PTR [88+rbp], eax • cmp r14, r13 • jne .lp ; Prob 82% • Začátek smyčky • .lp: • mov rcx, QWORD PTR [r14] • mov rax, QWORD PTR [rcx] • call QWORD PTR [8+rax] • Špatně predikovaný cíl volání • mov eax, DWORD PTR [4+rcx] • ret • Mikrooperace (odhad) • load eax,[8+rcx] • load t1,[rsp++] • jmp t1 • add r14,8 • load t2,[88+rbp] • add t2,eax • store [88+rbp],t2 • cmp r14,r13,eflags • jne .lp,eflags • load rcx,[r14] • load rax,[rcx] • load t3,[8+rax] • store [--rsp],rip • jmp t3 • load eax’,[4+rcx] • load t4,[rsp++] • jmp t4 NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  42. Odhad provedení kritického kódu s neúspěšnou predikcí 1: load eax,[8+rcx] 2: load t1,[rsp++] 3: jmp t1 4: add r14,8 5: load t2,[88+rbp] 6: add t2,eax 7: store [88+rbp],t2 8: cmp r14,r13,eflags 9: jne .lp,eflags 10: load rcx,[r14] 11: load rax,[rcx] 12: load t3,[8+rax] 13: store [--rsp],rip 14: jmp t3 1': load eax’,[4+rcx] 2': load t4,[rsp++] 3': jmp t4 NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  43. Odhad provedení kritického kódu s úspěšnou predikcí 1: load eax,[8+rcx] 2: load t1,[rsp++] 3: jmp t1 4: add r14,8 5: load t2,[88+rbp] 6: add t2,eax 7: store [88+rbp],t2 8: cmp r14,r13,eflags 9: jne .lp,eflags 10: load rcx,[r14] 11: load rax,[rcx] 12: load t3,[8+rax] 13: store [--rsp],rip 14: jmp t3 NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  44. Výsledky (Intel Core Microarchitecture, 2.66 GHz) NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  45. Hodnocení výsledků NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek • Náhodně promíchané objekty • Neúspěšná predikce skoku • Zpoždění načítání instrukcí + zátěž spekulativně provedenými instrukcemi • Odhad 20 cyklů = 7.5 ns, měření 9-13 ns (cache misses) • Objekty uspořádané do skupin podle druhu • Zlepšuje predikci skoků • U malých dat neúčinné – procesor se nestíhá naučit • Zůstává režie nepřímého volání (ověření predikce) - zatěžuje procesor • Odhad 8 cyklů = 3 ns, měření 3-8 ns (cache misses)

  46. Jiný přístup • class Apple{ • int seeds() { return d2; } • int d1, d2, d3; • }; • class Orange { • int seeds() { return d3; } • int d1, d2, d3, d4, d5; • }; • vector< Apple> dataA; • vector< Orange> dataO; • int s = 0; • for_each( dataA.begin(), dataA.end(), • [&]( Apple & x) { s += x.seeds(); }); • for_each( dataO.begin(), dataO.end(), • [&]( Orange& x) { s += x.seeds(); }); • Bez dědičnosti • Volání nebudou nepřímá • Překladač volané funkce integruje • Odpadá režie volání • Dovoluje další optimalizace • Bez ukazatelů • Ušetříme 1 přístup do paměti • Data jsou těsně vedle sebe • Nulová režie • Hardware prefetch • Použitelné pro operace nezávislé na pořadí • Pro udržení pořadí je zapotřebí další struktura • Pro operace závislé na pořadí neušetříme NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  47. Výsledky (Intel Core Microarchitecture, 2.66 GHz) NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  48. Sloupcově orientovaný přístup • vector< int> Ad1, Ad2, Ad3; • vector< int> Od1, Od2, Od3, Od4, Od5; • int s = 0; • for_each( Ad2.begin(), Ad2.end(), • [&]( int x) { s += x; }); • for_each( Od3.begin(), Od3.end(), • [&]( int x) { s += x; }); • Uložení po sloupcích • Umožňuje použití SIMD instrukcí • Čtená data jsou těsně vedle sebe • Lepší využití cache • Nevýhody • Ignoruje výhody objektového programování (enkapsulace) • Data jednoho objektu jsou rozptýlena • Horší využití cache při náhodném přístupu • Moderní databázové stroje používají sloupcový přístup NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  49. Sloupcově orientovaný přístup • vector< int> Ad1, Ad2, Ad3; • vector< int> Od1, Od2, Od3, Od4, Od5; • int s = 0; • for_each( Ad2.begin(), Ad2.end(), • [&]( int x) { s += x; }); • for_each( Od3.begin(), Od3.end(), • [&]( int x) { s += x; }); • SIMD instrukce • Intel/AMD: MMX/SSE/AVX/... • Některé překladače je dokážou vygenerovat samy • Jinde existují knihovní funkce skrývající SIMD instrukce • Použití vyžaduje znalost těchto instrukcí • Problém: zarovnání • Data pro SIMD instrukci se načítají obvykle atomicky • Vyžadují zarovnání větší než obvykle (SSE: 16 B) • Problém: nedělitelné zbytky na koncích polí • Zvýšená režie cyklu • SIMD se nevyplatí pro malá pole NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

  50. Sloupcově orientovaný přístup – SSE3 • #include <emmintrin.h> • vector< __m128i> Ad1, Ad2, Ad3; • std::size_t reserve; • __m128i ss = 0; • for_each( Ad2.begin(), Ad2.end() - 1, • [&]( __m128i & x) { • ss = _mm_add_epi32( ss, x); • }); • int s = ss.m128i_i32[ 0] • + ss.m128i_i32[ 1] • + ss.m128i_i32[ 2] • + ss.m128i_i32[ 3]; • for_each( Ad2.back().m128i_i32, • Ad2.back().m128i_i32 + 4 - reserve, • [&]( int x) { s += x; }); • Microsoft VC++/Intel C++ • Knihovní funkce skrývající SIMD instrukce • Baleny jednotlivě • Překladač je zná a integruje • Překladač sám přiděluje registry • Použití vyžaduje znalost těchto instrukcí • jednodušší než programování v assembleru • __m128i je 128-bitový typ odpovídající SSE registru • Překladač (někdy) zajistí zarovnání • _mm_add_epi32 odpovídá SIMD instrukci PADDQ • 4-krát součet 32-bitových čísel NPRG054 Vývoj vysoce výkonného software - 2013/2014 David Bednárek

More Related