home *** CD-ROM | disk | FTP | other *** search
/ Chip 2000 January / Chip_2000-01_cd.bin / obsahy / Chip_txt / TXT / 170.txt < prev    next >
Text File  |  1999-11-24  |  13KB  |  107 lines

  1. Optimalizace programà
  2. Od programu obvykle chceme, aby zabíral co nejmén╪ místa a p²itom aby b╪ºel co nejrychleji. Toho lze, jak známo, dosáhnout jeho optimalizací a snad kaºd∞, kdo alespoσ trochu p²iƒichl k programování, vysype toto obecné tvrzení z rukávu. Horτí uº to bude s odpov╪dí na otázku, co to ta optimalizace vlastn╪ je.
  3.  
  4. Dobr∞, lepτí, nejlepτí
  5.  
  6. Neº se do úvah o optimalizaci pustíme, rád bych upozornil, ºe i kdyº je následující povídání zaloºeno p²edevτím na zkuτenostech s jazyky C a C++, mnohé z jeho záv╪rà platí i obecn╪.
  7.  
  8. Velikost versus rychlost
  9. Na první pohled by se mohlo zdát, ºe ze dvou programà, které ²eτí t∞º problém, bude ten menτí také rychlejτí. Dále si ukáºeme, ºe to obecn╪ neplatí, ale urƒitá souvislost tu p²ece jenom je, zejména u opravdu rozsáhl∞ch programà.
  10. Dneτní operaƒní systémy pracují s tzv. virtuální pam╪tí, coº je mechanismus, kter∞ umoºσuje vyuºívat ƒást diskového prostoru jako operaƒní pam╪£, tedy RAM. (Mechanismy pro podporu virtuální pam╪ti jsou dnes zpravidla zabudovány p²ímo v procesoru. Mají je nap². i procesory Intel 80386 a pozd╪jτí.) Program se tedy nemusí cel∞ vejít do operaƒní pam╪ti, n╪které ƒásti (tzv. stránky) kódu nebo dat mohou b∞t uloºeny na disku. Program pak p²i b╪hu "swapuje": p²i v∞padku stránky (page fault), tj. poºadavku na data nebo kód, kter∞ není v operaƒní pam╪ti, p²eƒte operaƒní systém pot²ebné stránky z disku a p²ípadn╪ odloºí zp╪t ty, které nepot²ebuje, ale které se p²edtím zm╪nily.
  11. P²itom je ovτem podstatné, ºe p²ístup na disk je o n╪kolik ²ádà pomalejτí neº p²ístup do operaƒní pam╪ti. Proto ƒím v╪tτí je program, tím v╪tτí je i pravd╪podobnost v∞padku stránky, a tak se màºe jinak kvalitní program stát prakticky nepouºiteln∞m, jestliºe zabírá p²íliτ mnoho místa, nebo jestliºe i jinak "rozumn╪" velik∞ program spustíme na poƒítaƒi s p²íliτ malou operaƒní pam╪tí.
  12.  
  13. T²i oblasti optimalizace
  14. Zamyslíme-li se nad problémem optimalizace obecn╪ji, zjistíme, ºe màºeme hovo²it o t²ech oblastech, ve kter∞ch se p²ekladaƒ a programátor v ràzném pom╪ru d╪lí o kompetence. První oblast se t∞ká volby algoritmà p²i ²eτení problému a spadá v∞luƒn╪ do pravomoci programátora. V této oblasti lze získat nebo ztratit zdaleka nejvíce. Druhá oblast zahrnuje spolupráci programátora s p²ekladaƒem; jde o vyuºití registrov∞ch prom╪nn∞ch, voleb pro optimalizaci atd. Do t²etí oblasti pak lze zapoƒítat optimalizace zaloºené na specifick∞ch vlastnostech strojního kódu cílového poƒítaƒe - ty uº pat²í v∞luƒn╪ do kompetence p²ekladaƒe.
  15. V∞znam t²etí oblasti - stejn╪ jako druhé - je menτí neº v∞znam oblasti první, to ale neznamená, ºe je vºdy zanedbateln∞. Uplatní se hlavn╪ u dlouh∞ch programà, u programà obsahujících cykly s velk∞m poƒtem opakování apod. Povídání o druhé oblasti vτak bude nejrozsáhlejτí, a proto si je necháme aº na konec.
  16.  
  17. Optimalizace nejvyττí úrovn╪
  18. Zeptáte-li se programátora, proƒ je jeho program tak pomal∞, ƒasto uslyτíte n╪jak∞ povzdech na téma nedokonalého p²ekladaƒe, kter∞ vàbec neumí optimalizovat, nedokonalého operaƒního systému a bàhví ƒeho jeτt╪. Málokdy uslyτíte o nedokonalém programátorovi, kter∞ nedokáºe vybrat optimální algoritmus - a p²ece práv╪ to màºe b∞t p²íƒinou pomalého b╪hu programu. Zisky nebo ztráty v této oblasti jsou totiº obvykle zdaleka nejv∞razn╪jτí.
  19. Typick∞m p²íkladem mohou b∞t algoritmy pro t²íd╪ní polí, tj. pro p²erovnání jejich prvkà podle velikosti. Ze základního kurzu programování víme, ºe sloºitost algoritmu t²íd╪ní haldou (heap sort) nebo rychlého t²íd╪ní (quick sort) je v p²ípad╪ pole o n prvcích rovna hodnot╪ O(n*ln n), tzn. je úm╪rná ƒíslu n*ln n, zatímco sloºitost jednoduττích metod, jako je t²íd╪ní vkládáním (insertion sort), je O(n2). V╪c se tedy zdá jasná: zapomeneme na jednoduché metody, které se sice snadno uƒí a snadno programují, ale jinak za mnoho nestojí, a budeme pouºívat jen metody efektivní, by£ sloºité.
  20. Zdání ovτem màºe klamat. Pro t²íd╪ní polí s mal∞m poƒtem prvkà mohou totiº b∞t jednoduché metody v∞hodn╪jτí. Postavme si vedle sebe n╪kolik podobn∞ch (a zdánliv╪ ekvivalentních) úloh:
  21. - set²ídit jedno jediné celoƒíselné pole o 1 000 000 prvkà,
  22. - set²ídit 10 celoƒíseln∞ch polí o 100 000 prvkà,
  23. - set²ídit 100 celoƒíseln∞ch polí o 10 000 prvkà
  24. atd.
  25. I kdyº ve vτech p²ípadech t²ídíme milion cel∞ch ƒísel, v∞sledky ràzn∞ch t²ídicích metod se budou v∞razn╪ liτit. P²ipojená tabulka ukazuje pràm╪rné ƒasy v sekundách pot²ebné pro tyto úlohy p²i t²íd╪ní vkládáním a p²i t²íd╪ní haldou. Vidíme, ºe pro velmi malá pole màºe b∞t v∞hodn╪jτí t²íd╪ní vkládáním (které p²edstavuje více jednoduch∞ch operací), neº t²íd╪ní haldou, které znamená sice mén╪ operací, ovτem podstatn╪ sloºit╪jτích.
  26.  
  27. Optimalizace nejniºτí úrovn╪
  28. Tato oblast optimalizace vyuºívá specifik strojního kódu cílového procesoru. Mnohé konstrukce z vyττích programovacích jazykà lze p²eloºit n╪kolika zpàsoby. Efekt je stejn∞, tedy program ud╪lá totéº, ovτem v∞sledn∞ kód je podle okolností menτí nebo rychlejτí.
  29. Jako p²íklad vezmeme procesory jedné z nejrozτí²en╪jτích znaƒek - Intel. Chceme-li nap². do prom╪nné X typu int uloºit hodnotu 0, màºeme pouºít bu╘ instrukci
  30. MOV dword ptr[X],0
  31. nebo
  32. AND dword ptr[X],0
  33. V∞sledek, tedy stav dat, je v obou p²ípadech stejn∞, avτak první instrukce zabere 6 bajtà a trvá 3 takty, zatímco druhá zabere 9 bajtà a trvá 1 takt. Zde tedy stojíme p²ed volbou rychlost nebo velikost, nemàºeme vτak mít obojí.
  34. V tomto p²ípad╪ màºeme uτet²it na jedné instrukci 2 takty. Abychom si uv╪domili souvislosti, p²ipomeσme si, ºe procesor s taktovací frekvencí 100 MHz - ale tak "zastaralé" procesory se uº snad ani nedají sehnat - provede za sekundu práv╪ sto milionà taktà. Abychom tedy zrychlili b╪h programu o jedinou sekundu, musíme uτet²it stovky milionà taktà. To je moºné, pokud program obsahuje nap²íklad velmi rozsáhlé cykly.
  35. Je asi jasné, ºe optimalizace na této úrovni je v∞luƒnou záleºitostí p²ekladaƒe - samoz²ejm╪ pokud nechceme programovat v asembleru a neustále porovnávat ƒasovou a prostorovou nároƒnost kódu.
  36.  
  37. St²ední úroveσ optimalizace
  38. Do této oblasti spadají optimalizaƒní techniky, na kter∞ch spolupracuje p²ekladaƒ s programátorem. V╪tτinou to znamená, ºe programátor p²ekladaƒi pomocí voleb nebo klíƒov∞ch slov naznaƒí, kterou z moºností má p²ekladaƒ pouºít a co màºe p²ekladaƒ o programu p²edpokládat. Obvykle ale màºe zasáhnout programátor a provést odpovídající úpravy na úrovni zdrojového kódu sám.
  39. Tyto techniky mohou, ale nemusí vést k cíli. N╪které lze pouºít jen za jist∞ch p²edpokladà, jiné mohou n╪kdy dokonce situaci zhorτit. ¼asto je t²eba zkusmo urƒit, které z nich mají a které nemají v daném p²ípad╪ ºádan∞ úƒinek. Podíváme se na n╪které z nejƒast╪ji pouºívan∞ch zpàsobà.
  40. Sluƒování cyklà
  41. Pokud se v programu vyskytují dva cykly se stejn∞m rozsahem a s nezávisl∞m obsahem, je rozumné je slouƒit do jednoho cyklu (loop jamming). Chceme-li nap². vynulovat dv╪ pole a  a b o N prvcích, màºeme napsat
  42. for(int i = 0; i < N; i++) a[i] = 0;
  43. for(int i = 0; i < N; i++) b[i] = 0;
  44. ale rozumn╪jτí je slouƒit oba cykly do jednoho, 
  45. for(int i = 0; i < N; i++)
  46. {
  47. a[i] = 0;
  48. b[i] = 0;
  49. }
  50. nebo£ se tím uτet²í "administrativa", tj. instrukce, které ²ídí opakování cyklu.
  51.  
  52. Práce s registry
  53. Pouºívání klíƒového slova register pat²í k nejstarτím optimalizaƒním moºnostem, zaveden∞m jiº v jazyce C podle Kernighana a Ritchieho. Jist╪ si vzpomenete, ºe toto klíƒové slovo má za úkol naznaƒit p²ekladaƒi, ºe práv╪ deklarovanou prom╪nnou nebo parametr budeme dále ƒasto pouºívat a ºe by se m╪l pokusit uloºit ji do n╪kterého z registrà procesoru; p²ístup k registràm je totiº zpravidla podstatn╪ rychlejτí neº p²ístup do pam╪ti.
  54. Dnes uº je vτak toto klíƒové slovo tém╪² zbyteƒné, nebo£ v╪tτina p²ekladaƒà pouºívá rafinované algoritmy, pomocí nichº urƒí, které prom╪nné je vhodné uloºit do registrà (a obvykle to odhadne lépe neº programátor). P²ekladaƒe ƒasto dávají programátorovi na vybranou, zda chce, aby se ²ídily jeho poºadavky, aby registrové prom╪nné nepouºívaly vàbec (ani tam, kde je to v programu p²edepsáno - to se hodí p²i lad╪ní), nebo aby je p²ekladaƒ pouºíval dle vlastního uváºení. Poslední moºnost zpravidla vede k nejlepτím v∞sledkàm.
  55.  
  56. Vloºené funkce
  57. Pouºívání vloºen∞ch funkcí (inline) není (zatím) souƒástí jazyka C, najdeme je aº v jazyce C++, ale ve skuteƒnosti jde o jeden z nejstarτích optimalizaƒních trikà, které mohl programátor pouºívat. S jejich obdobou jsme se mohli setkat nap². uº v dávn∞ch verzích Fortranu (jednop²íkazové funkce - statement function).
  58. Volání b╪ºné funkce znamená vºdy jistou reºii. P²i volání je to uloºení parametrà a návratové adresy do zásobníku, vytvo²ení rámce zásobníku a lokálních prom╪nn∞ch atd., p²i návratu mj. p²edání návratové hodnoty, odstran╪ní lokálních prom╪nn∞ch a skuteƒn∞ch parametrà atd.
  59. Vloºené funkce, tj. funkce s modifikátorem inline, se sice po syntaktické stránce chovají jako funkce, ale p²ekladaƒ je pouºívá podobn╪ jako makra. To znamená, ºe na místo volání vloºí t╪lo této funkce. To zpravidla vede k rozsáhlejτímu, ale rychlejτímu programu.
  60. Modifikátor inline ovτem - podobn╪ jako modifikátor register - není pro p²ekladaƒ závazn∞. To znamená, ºe p²ekladaƒ màºe usoudit, ºe danou funkci není vhodné p²ekládat jako vloºenou, a bude s ní zacházet jako s obyƒejnou funkcí. Zapíτeme-li takovouto funkci do hlaviƒkového souboru, màºe se stát, ºe p²ekladaƒ vytvo²í její t╪lo v kaºdém ze souborà, ve kterém takov∞to hlaviƒkov∞ soubor pouºijeme. Pak pouºitím vloºen∞ch funkcí nezískáme ºádnou v∞hodu, pouze zv╪tτíme v∞sledn∞ program. (To ovτem není typická situace.)
  61. P²ekladaƒe obvykle dávají na vybranou moºnost klíƒové slovo inline ignorovat; to se hodí op╪t zejména p²i lad╪ní. Vedle toho nabízejí n╪které p²ekladaƒe také moºnost p²ekládat jako vloºené n╪které z knihovních funkcí, které jsou jinak p²ekládány "normáln╪" (obvykle se tato volba jmenuje inline intrinsic functions). 
  62. Poznamenejme jeτt╪, ºe p²ekladaƒ màºe v p²ípad╪ pot²eby pouºít funkci obojím zpàsobem - jako vloºenou i jako normální.
  63.  
  64. µí²ení konstant a kopií
  65. Tato optimalizaƒní technika je zaloºena na skuteƒnosti, ºe pouºití konstant v instrukcích je rychlejτí neº práce s registry a práce s registry je rychlejτí neº práce s operaƒní pam╪tí. µí²ení konstant (constant propagation) lze pouºít v situaci, kdy do jedné prom╪nné p²i²adíme konstantu a tuto hodnotu pak "poτleme dál", aniº bychom ji mezitím pouºili. To znamená, ºe pokud v programu napíτeme nap².
  66. a = 10; // (1)
  67. b = a; // (2)
  68. kde a, b jsou typu int, p²epíτe to p²ekladaƒ do tvaru
  69. a = 10;
  70. b = 10;
  71. P²itom mezi (1) a (2) mohou b∞t dalτí p²íkazy, nesm╪jí ovτem pracovat s hodnotu uloºenou do a. (Zde se tedy "rozτí²ila" konstanta 10.) 
  72. µí²ení kopií (copy propagation) je podobné: Jestliºe p²i²adíme do jedné prom╪nné hodnotu a tuto hodnotu pak "poτleme dál", aniº bychom ji mezitím pouºili, màºe si p²ekladaƒ ponechat v registru její hodnotu a tak urychlit b╪h programu tím, ºe uτet²í p²ístup do pam╪ti. Jestliºe tedy v programu napíτeme
  73. a = x;
  74. F(a);
  75. kde a  i x jsou stejného typu, p²epíτe to p²ekladaƒ do tvaru
  76. a = x;
  77. f(x);
  78.  
  79. Nepouºit∞ kód a nepouºitá pam╪£
  80. P²i optimalizaci (ale také vinou nepozornosti programátora, p²i rozvoji maker nebo τablon atd.) màºe nastat situace, ºe do prom╪nné uloºíme hodnotu, kterou program nikdy nepouºije. Takové p²i²azení lze z programu vypustit (store elimination). Podívejme se na uƒebnicov∞ p²íklad. Jestliºe ve zdrojovém textu napíτeme
  81. a = x;
  82. f(a);
  83. a = y;
  84. a p²ekladaƒ pouºije techniku τí²ení kopií, vznikne
  85. a = x; // (1)
  86. f(x);
  87. a = y;
  88. P²i²azení hodnoty prom╪nné a  je naprosto zbyteƒné, samoz²ejm╪ za p²edpokladu, ºe a není globální prom╪nná a funkce f() ji nepouºívá. To ale znamená, ºe p²ekladaƒ màºe p²íkaz (1) prost╪ odstranit a p²epsat tento úsek programu do tvaru
  89. f(x);
  90. a = y;
  91. Podobn╪ se v programu màºe objevit nepouºit∞ (mrtv∞) kód (dead code) - úsek programu, kter∞ nemàºe b∞t nikdy provád╪n. P²ekladaƒ ho màºe odstranit. Tím se sice program nezrychlí, ale zmenτí, a to màºe za jist∞ch okolností také vést ke zrychlení.
  92.  
  93. Spoleƒné podv∞razy
  94. Tato optimalizace je podobná τí²ení kopií. Jestliºe se na n╪kolika místech v programu objeví t∞º v∞raz nebo dílƒí v∞raz, màºe p²ekladaƒ p²epsat program tak, ºe se tento podv∞raz vypoƒte jen jednou a jeho hodnota se uloºí do pomocné prom╪nné. To znamená, ºe nap². následující úsek programu
  95. x = (a+b)*c;
  96. y = sin(a+b);
  97. p²epíτe do tvaru
  98. pom = a+b;
  99. x = pom*c;
  100. y = sin(pom);
  101. Je urƒit╪ z²ejmé, ºe eliminace spoleƒn∞ch podv∞razà (common subexpression elimination) povede tém╪² vºdy ke zrychlení programu. Za jist∞ch okolností - jestliºe se podv∞razy objevují dostateƒn╪ ƒasto - màºe zpàsobit i zmenτení programu.
  102.  
  103. P²íτt╪
  104. Tolik pro dneτek - p²íτt╪ si povíme o n╪kter∞ch dalτích optimalizaƒních technikách a p²ipojíme i n╪kolik obecn∞ch úvah o v∞znamu optimalizace.
  105. Miroslav Virius
  106.  
  107.