• Assembler pro začátečníky ][ ••

              (Proč používáme assembler - základy optimalizace)

           V  první  části mojí miniučebnice assembleru, jsem vás seznámil
     se základy programovaní v assembleru. Ale asi uznáte, že psát všechny
     programy  kompletně  v  assembleru  je  velice  neefektivní  a mnohdy
     zbytečné.  Proto  většina  z nás využívá vymožeností hi-level jazyků.
     Chci se proto zaměřit na použití assembleru v hi-level jazycích, tedy
     hlavně  pro  low-level  optimalizaci  programů.  Základem efektivního
     využívaní  optimalizace je přibližná představa o tom, jak kompilátor,
     kterým  svůj  program  kompilujete, generuje výsledný kód. Pokud jste
     provedli  všechny  hi-level  optimalizace  jako odstranění zbytečných
     volání  funkcí  ve  výkonné  smyčce a nahradili časově náročné funkce
     předpočítanými tabulkami, je na řadě přepsat časově kritická místa do
     assembleru.  Prvním krokem optimalizace je vyloučit zbytečné přístupy
     do  paměti  a  všechny  potřebné  konstanty a výpočty provádět pomocí
     registrů  procesoru. Dalším, výrazně urychlujícím krokem je nahrazení
     časově  náročné  instrukce  jako například mul a div instrukcemi jako
     shl,  shr  nebo  xchg.  Jejich  taktování je o proti mul a div až 30x
     menší.  Používají  se  především  v případech, kdy se nějaká proměnná
     násobí konstantou.

                                   Y:=2*X

                   Pascal (8-bit)        Assembler (8-bit)

                   mov al,2  {04}         mov al,X {08}
                   mov bl,X  {08}         shl ax,1 {02}
                   mul bl    {72}         mov Y,ax {09}
                   mov Y,al  {09}
                            ------                 -----
                              93                    19

           Tímto  způsobem  se  dají   velice  rychle  vynásobit  proměnné
     hodnotami 2^n tedy (2,4,16,32,64,...).  Pokud není konstanta mocninou
     2, je  třeba použít dvou  instrukcí. Pro příklad  uvedu násobení 320,
     které se hojně používá v režimu 320x200x256.

                                   Y:=320*X

                   Pascal (16-bit)       Assembler (16-bit)

                   mov ax,320 {004}         mov  ax,X   {08}
                   mov bx,X   {008}         xchg al,ah  {04}
                   mul bx     {124}         mov  bx,ax  {02}
                   mov Y,ax   {009}         shr  bx,2   {16}
                                            add  bx,ax  {03}
                                            mov  Y,bx   {09}
                             ------                    -----
                               145                       42

           Násobení  320 lze také provést například instrukcemi shl ax,6 a
     shl  bx,8  pokud pracujete s proměnnými typu integer. Z obou příkladů
     je  vidět,  že  takováto optimalizace dokáže urychlit výsledný průběh
     programu  až  pětkrát, takže pokud se vám ve vnitřní smyčce vyskytují
     podobné  konstanty,  nahraďte je výše zmíněnými instrukcemi a získáte
     tak  cenné  takty  pro  časově  náročné  mul  a div, které už jen tak
     neodstraníte.  Za  zmínku stojí i fakt, že v současné době už většina
     programátorů  optimalizuje  pro  Pentium,  protože  má jednu výbornou
     vlastnost  pokud  mu  pošlete  ke  zpracování  po  sobě jdoucí stejné
     instrukce,  vykoná  je  najednou,  takže  pokud  se  ve  vaší  smyčce
     vyskytuje  například více mul a div instrukcí je dobré je pokud možno
     zařadit  za  sebe.  Další výrazné urychlení docílíte vhodným použitím
     instrukcí typu movs a stos. Používají se při hromadném přesouvání dat
     nebo  vyplňování  paměťových  oblastí  stejnou  hodnotou.  Používejte
     instrukce  s  největším  objemem dat jako movsd a stosd, pokud nejste
     omezeni procesorem. Při přepisování si ani neuvědomíte, že už vlastně
     optimalizujete, protože Pascal některé části programu generuje velice
     obecně, takže pokud využijete například hodnoty v registru ax pro tři
     operace  získáte  tím až 20 taktové urychlení. Dále je velice výhodné
     (pokud  to  jde) optimalizovat v každé situaci, takže notoricky známe
     xor ax,ax místo mov ax,0 nebo inc ax, inc ax místo add ax,2 se určitě
     vyplatí. Problematika low-level optimalizace je velice zajímavá věc a
     určitě stojí za to se jí více zabývat. V následujících příkladech vám
     přináším  ukázku,  jak  by  asi  měla vypadat optimalizace jednoduché
     funkce  na  kreslení  čáry  pomocí  Bresenhamova  algoritmu. Nejdříve
     uvádím   její  hi-level  podobu  a  následuje  vysoce  optimalizovaná
     procedura  pro  speciální  případ  horizontální  čáry  a pak pro čáru
     nakreslenou  mezi  dvěma obecnými body [x1,y1] a [x2,y2]. Takže si je
     pilně prostudujte a možná přijdete i na to, jak by se daly ještě více
     zoptimalizovat,  je  to  jen  otázka nápadu. Na úplný závěr přikládám
     malý  rezidentní  prográmek  v  Pascalu,  který  má  jednu  zajímavou
     vlastnost,  pokud  jej  spustíte,  asi  si už nezahrajete nějakou hru
     běžící  v  režimu  320x200x256. Je to docela dobrý joke pro notorické
     hráče,  zkuste  jej  nenápadně  vepsat  do  autoexecu nějakému vašemu
     známému,   který   tráví  většinu  svého  volného  času  hraním  her,
     pravděpodobně mu tím způsobíte abstinenční záchvat.

                                                              ReDox

                                -> příklady <- 

                                  no13h.pas 


            výheň