Sylabus V rámci PNV budeme řešit konkrétní úlohy a to z následujících oblastí: Nelineární úlohy –Řešení nelineárních rovnic –Numerická integrace Lineární úlohy –Řešení soustav lineárních rovnic –Metoda nejmenších čtverců pro lineární úlohy –Sumace obecné a s korekcí Numerické výpočty v C a C++ –Optimalizace výrazů, optimalizace při překladu
Optimalizace programu a optimalizující překladače Klasické optimalizace Optimalizující překladače Úpravy programu, umožňující efektivnější činnost překladače
Klasické optimalizace - obecně Jednoduché transformace Působí lokálně nebo globálně Jsou realizovány základě analýzy toku dat (data flow) v rámci proměnných
Klasické optimalizace - propagace kopírováním Odstraňují závislosti proměnných v rámci kódu, které by bránily paralelizaci tohoto kódu. Příklad:Řešení:X = Y Z = 1 + XZ = 1 + Y
Klasické optimalizace - zpracování konstant Operace nad konstantami se vyhodnotí již v době překladu. Příklad: 3*5pracuje s 15 i = 3; j = 5; k = i*j;pracuje s k = 15
Klasické optimalizace - zpracování konstant II Problémy: Přesnost konstant Příklad: a * b * c a, b konstanty (a - b) * ca, b konstanty Přetypování (například real a int) a * bint * int (může vést k přetečení) Dělení nulou: x = 0; j = k/x;kompilátor provede rozvoj, ? jak reagovat
Klasické optimalizace - odstraňování mrtvého kódu Kód, který nebude nikdy využit. Příklad: return x; k = 8; Příklad: Kód if nebo else větví podmínek, u kterých je po vyhodnocení podmínek zřejmé, že do nich program nevstoupí.
Klasické optimalizace - strenght reduction Snížení obtížnosti výpočtu - nahrazení složitějších operací jednoduššími Příklad: a 2 nahradíme a*a 2*anahradíme a+a (v dřívějších procesorech) Obecně: a k, kde k je celočíselné a malé se implicitně nahrazuje a*a*...*a. Proto lépe a 2 než a 2.0.
Klasické optimalizace - přejmenování proměnných Odstraňuje závislosti mezi proměnnými => umožňuje efektivněji interně paralelizovat kód. (Podobné jako u propagace kopírováním, jen jiný mechanismus.) Příklad: x = y * z;x0 = y * z; q = r + x + x;=>q = r + x0 + x0;x = a + b;
Klasické optimalizace - odstraňování společných podvýrazů Několik výrazů obsahuje společný podvýraz. => Podvýraz se vypočte pouze jedenkrát, jeho hodnota se použije i v dalších výrazech. Příklad: D = C * (A + B)T = A + B E = (A + B) / 2D = C * T E = T / 2
Klasické optimalizace - odstraňování společných podvýrazů II Mez rozeznání: Rozezná: a+c = c+a Nerozezná: a+b+c, a+c+b,... Důvod: Porovnávání řetězců neomezené délky má velkou složitost. Výrazy a+b+c, a+c+b se mohou lišit vlivem zaokrouhlení.
Klasické optimalizace - odstraňování společných podvýrazů III Nejužitečnější je při výpočtu adres prvků v polích. (Tyto výpočty provádí kompilátor.) Příklad: adresa prvku A(I, J): address(A) + (I-1)*sizeof_datatype(A) + + (J-1)*sizeof_datatype(A) + column_dimension(A) pokud s tímto prvkem pracujeme častěji, bude kompilátor výše uvedené považovat za podvýraz a uloží si tuto hodnotu do pomocné proměnné
Klasické optimalizace - přesun invariantního kódu z cyklů = kód, jehož hodnota se nemění Příklad: for (i = 0; i < n; i++) { angle = fi*2*pi/360; a[i] = b[i]*sin(angle); }
Klasické optimalizace - zjednodušení indukčních proměnných lze považovat za speciální případ odstranění invariantního kódu z cyklů A(I) je většinou počítáno jako: address = base_address(A) + (I-1)*sizeof_datatype(A) to lze zjednodušit na: mimo cyklus: address = base_address(A) - sizeof_datatype(A) v cyklu: address = address + sizeof_datatype(A)
Klasické optimalizace - přiřazení registrů proměnným Rozdělit proměnné na často využívané a ostatní. Často využívané držet v registrech. V jazyce C existuje přímo typ register.
Optimalizující překladače - obecně Cíl: výchozí jazyk cílový jazyk Reálný průběh: výchozí jazyk mezijazyk cílový jazyk
Optimalizující překladače - mezijazyk Využívá zápis instrukce pomocí čtveřic (n-tic) čtveřice obecně: (operand, operátor1, operátor2, výsledek) příklad: instrukce:x := y + z čtveřice: (+, y, z, x)
Optimalizující překladače - mezijazyk II Pro ukládání hodnot do paměti využívá dočasné proměnné t n Příklad: Výraz: A = -B + C * D / E Zápis v mezijazyce: T1 = D / E T2 = C * T1 T3 = -B A = T3 + T2
Optimalizující překladače - mezijazyk III Další vlastnosti mezijazyka: Podmínky skoků jsou vyhodnoceny. Skoky vedou na absolutní adresy.
Optimalizující překladače - příklad převodu do mezijazyka while (j < n) { k = k + j * 2; m = j * 2; j ++; } B::t4 := k t5 := j t6 := t5 * 2 t7 := t4 + t6 k := t7 t8 := j t9 := t8 * 2 m := t9 t10 := j t11 := t j := t11 jmp (A) TRUE C:: A::t1 := j t2 := n t3 := t1 < t2 jmp (B) t3 jmp (C) TRUE
Optimalizující překladače - optimalizace mezijazyka Optimalizační metody: –Lokální (v rámci jednoho bloku) –Globální optimalizace
Optimalizující překladače - mezijazyk - základní bloky Kód v mezijazyce lze rozdělit na tzv. základní bloky. Základní blok je část kódu, pro kterou platí: První instrukce bloku následuje po větvení. Poslední instrukce kódu vedou ke větvení. Žádné jiné než poslední instrukce větvení nezpůsobují.
Optimalizující překladače - mezijazyk - základní bloky II B::t4 := k t5 := j t6 := t5 * 2 t7 := t4 + t6 k := t7 t8 := j t9 := t8 * 2 m := t9 t10 := j t11 := t j := t11 A::t1 := j t2 := n t3 := t1 < t2 jmp (B) t3 jmp (C) TRUE C:: jmp (A) TRUE
Optimalizující překladače - optimalizace základních bloků Ukázka intuitivní optimalizace bloku B (= obsah cyklu while): while (j < n) { k = k + j * 2; m = j * 2; j ++; } while (j < n) { m = j * 2; k = k + m; j ++; }
Optimalizující překladače - optimalizace základních bloků II Pro optimalizaci bloků lze využít například zápis kódu z těchto bloků pomocí orientovaného acyklického grafu (directed acyclic graph, DAG). Orientace DAG: Od potomků k rodičům.
Optimalizující překladače - optimalizace základních bloků III List DAG: –konstanta –proměnná + všechny t i, ve kterých byla tato proměnná uložena Nelistový uzel DAG: –Ohodnocen operátorem –Pro potomky A a B obsahuje proměnné, ve kterých je uložena hodnota výrazu: A operátor B
Optimalizující překladače - optimalizace základních bloků - příklad "2" k, t4j, t5, t8, t10 "1" * ++ m, t 6, t 9 k, t 7 j, t 11
Optimalizující překladače - optimalizace základních bloků IV Takto získaný DAG vyjadřuje sémantiku daného základního bloku. Daný DAG přepíšeme do mezijazyka následovně: Pro list, obsahující proměnné: x, ti, tj, tk,... zapíšeme instrukci: ti := x
Optimalizující překladače - optimalizace základních bloků V Pro nelistový uzel, který: –má potomky, přiřazující do ta a tb –je označený operátorem * –obsahuje proměnné: ti, tj, tk,... a případně i proměnnou x zapíšeme instrukci (instrukce): ti := ta * tb(pokud neobsahuje x) ti := ta * tb(jinak) x := ti
Optimalizující překladače - optimalizace základních bloků - příklad II Optimalizovaný kód bloku B: t4 := k t5 := j t6 := t5 * 2 m := t6 t7 := t6 + t4 k := t7 t11 := t5 + 1 j := t11 jmp (A) TRUE
Optimalizující překladače - globální optimalizace Po vytvoření DAG může kompilátor vygerovat pro každý blok seznam nadefinovaných a využitých proměnných. Pomocí těchto seznamů lze: –Přesunout definice až do bloků, kde jsou nutné –Rozpoznat případy, kdy jedna proměnná je využita ve dvou nezávislých případech –Určit, kdy není nutno provádět operace load a store nad registry
Optimalizující překladače - globální optimalizace - příklad while (j < n) { m = j * 2; k = k + m; j ++; } Odstranění inkrementace proměnné z těla cyklu: m = j * 2; nn = n * 2; while (m < nn) { k = k + m; m = m + 2; }
Optimalizující překladače - globální optimalizace - příklad II Optimalizovaný kód: A::t1 := j; t2 := n; t3 := k; t4 := t1 + t1; t5 := t2 + t2; t6 := t4 < t5 jmp (B) t6 B::t3 := t3 + t4; t4 := t4 + t4; t6 := t4 < t5; jmp (B1) t6 B1::k := t3; m := t4; C:: Poznámka: Navíc byly složitější operace nahrazeny jednoduššími.
Optimalizující překladače - cykly Nejvýrazněji se v nich projevují optimalizace. Typy cyklů: Explicitní cykly (for, while) Cykly vzniklé voláním GOTO Rekurze
Úpravy programu, umožňující efektivnější činnost překladače - podprogramy Znesnadňují interní paralelizaci Příklad: řešení:makra, inlining, IPA DO 10 I=1,N A[I] = A[I] + B[I] * C; CONTINUE DO 10 I=1,N CALL SHORT (A[I], B[I], C) CONTINUE... SUBROUTINE SHORT (A, B, C) A = A + B * C RETURN END
Úpravy programu, umožňující efektivnější činnost překladače - podmíněné výrazy Reorganizace výrazů: vyhodnocování standardně zleva doprava vyhodnocování končí, když je znám jeho výsledek Příklady: A OR B OR C pokud je A TRUE, je zřejmé, že celý výraz je také TRUE A AND B AND C pokud je A FALSE, je zřejmé, že celý výraz je také FALSE
Úpravy programu, umožňující efektivnější činnost překladače - podmíněné výrazy II U disjunkcí umístit podvýrazy, co má velkou pravděpodobnost TRUE umístit doleva. U konjunkcí to, co má velkou pravděpodobnost FALSE umístit doleva. Některé programovací jazyky (např. C) dodržují pořadí, dané programem. Jiné programovací jazyky vyhodnocují v náhodném pořadí (FORTRAN) nebo se snaží o analýzu podvýrazů.
Úpravy programu, umožňující efektivnější činnost překladače - podmíněné výrazy III Nadbytečné výrazy: Příklad: if (a == 7)... if (a == 8)... Důvod: FORTRAN neznal else.
Úpravy programu, umožňující efektivnější činnost překladače - podmíněné výrazy III Řešení: –Aritmetické IF: Syntaxe: IF Výraz Návěstí_1 Návěstí_2 Návěstí_3 Sémantika:Výraz < 0:GOTO Návěstí_1 Výraz = 0:GOTO Návěstí_2 Výraz < 0:GOTO Návěstí_3 Velmi snižuje čitelnost kódu. –Konstrukce typu case –Pokud lze, tak využít IF, THAN, ELSE (respektive ELSIF)
Úpravy programu, umožňující efektivnější činnost překladače - podmíněné výrazy v cyklech Zpomalují výpočet: Musí se při každém průchodu cyklem vyhodnotit Každé větvení způsobuje rozpad pipeliny => odstranit (pokud to lze) Typy podmíněných výrazů v cyklech: –Nezávislé na cyklu –Závislé na cyklu Nezávislé na iteraci Závislé mezi iteracemi
Úpravy programu, umožňující efektivnější činnost překladače - podmíněné výrazy v cyklech II Výrazy nezávislé na cyklu: Nejsou závislé na řídící proměnné cyklu Příklad: for (i=0; i < n; i++) { a[i] = a[i]+b[i]*c; if(DEBUG == 1) printf("%f", b[i]); } Poznámka: Lze ještě zhoršit: extern DEBUG
Úpravy programu, umožňující efektivnější činnost překladače - podmíněné výrazy v cyklech III Příklad 2: for (i=0; i < n; i++) { if (abs(c) > e) a[i] = b[i]+a[i]*c; else a[i] = b[i]; } Řešení: if (abs(c) > e) { for (i=0; i < n; i++) { a[i] = b[i]+a[i]*c; } else for (i=0; i < n; i++) { a[i] = b[i]; }
Úpravy programu, umožňující efektivnější činnost překladače - podmíněné výrazy v cyklech IV Výrazy závislé na cyklu a nezávislé na iteraci: Jsou závislé pouze na řídící proměnné cyklu: Příklad: for (i=0; i < n; i++) { if (i > k) a[i] = a[i]+b[i]*c; else a[i] = 0; } Řešení: for (i=0; i <= k; i++) { a[i] = 0; } for (i=k+1; i < n; i++) { a[i]+b[i]*c; }
Úpravy programu, umožňující efektivnější činnost překladače - podmíněné výrazy v cyklech V Výrazy závislé na cyklu a závislé na iteraci: Jsou závislé na hodnotách proměnných v dané iteraci: Příklad: for (i=0; i < n; i++) { if (b[i] > k) a[i] = a[i]+b[i]*c; } Řešení: Rozvinutí cyklu for (i=0; i < n; i+2) { if (b[i] > k) a[i] = a[i]+b[i]*c; if(b[i+1] >k) a[i+1] = a[i+1]+b[i+1]*c; }