Nového psa starým trikům nenaučíš

Ptal se mě tuhle jeden čtenář na to, jestli bych někde nevyštrachal svou knížku o osmibitových tricích, protože by ho to zajímalo. Hlavně jestli se něco z toho dá použít ještě dneska… Tak: nedá! Proč?

[Tento článek vyšel před rokem původně na mém zápisníku, ovšem zaměřením patří jednoznačně sem, takže jsem ho přenesl…]

Doba osmibitová byla plná hrdinských činů, kdy mužové s očima rozedřenýma do krve od mihotavého světla mizerných přesvícených televizí ohýbali křemík až na samé hranice jeho možností. Měl jsem v té době ZX Spectrum a rád jsem se hrabal ve zdrojácích, abych viděl, jak pracují ti lepší a přiučil se od nich. Jo, tehdy optimalizace ještě něco znamenala a ušetřit sto bajtů (zhruba tak prostor, který zabírá text perexu tohoto článku) znamenalo mít možnost do nich naprat novou funkci. Stejně tak ušetřit pár taktů uvnitř cyklu… A tak se používaly nejrůznější triky.

Na Spectru jste mohli napsat v BASICu třeba tohle:

a do paměti se uložilo mnoho bajtů. V jednom byl kód tokenu LET, v dalších pak znaky „a“, „=“, „3“, „1“, a za tím speciální šestibajtová sekvence, ve které bylo uloženo to číslo (0x1f). Teď to píšu z hlavy, jsem líný to dohledávat, tak uvidíme, jak si to po dvaadvaceti letech pamatuju… Dohromady tedy 11 bajtů. Gut. Šlo by to zkrátit? Šlo!

První bajt byl token pro LET, další pak „a“, „=“, token pro VAL, uvozovka, „3“, „1“ a uvozovka. Tedy osm bajtů. Tedy tři uspořené. Když jste takhle zváleli všechny proměnné, ušetřili jste fakt slušný kus paměti. A některá čísla (0, 1, 3) šly ještě kratším zápisem: NOT PI, SIGN PI, INT PI – LET a=NOT PI zabralo 5 bajtů, LET a=0 deset. Rychlost? Prosímvás, v BASICu? 🙂

Když chcete rychlost, musíte do strojáku.

Tam jsme všichni uctívali velké T. Totiž takt procesoru. Nejkratší instrukce, třeba NOP, přesuny mezi registry nebo prostá aritmetika, zabraly čtyři T. Když jste potřebovali uložit číslo do paměti, mohli jste si vybrat:

A to jste si museli předtím připravit buď adresu, nebo obsah, nebo obojí do registrů. Když jste potřebovali smazat obrazovku, museli jste zapsat hodnotu 0 na adresy $4000-$57FF. Jedna možnost byla klasická s blokovou instrukcí LDIR:

což bylo ukrutně pomalé, protože instrukce LDIR sežere na uložení jedné hodnoty 21T. Celá obrazovka se tak mazala skoro 130.000T, takže jste mohli takhle smazat obrazovku zhruba 27krát za sekundu. Nic moc výkon. Nebylo by něco rychlejšího? Co takhle to rozepsat do cyklu? LD (HL), A zabere přeci jen 7T… ale zase nějaký čas sežere režie smyčky.

Po nějakém čase, stráveném s dokumentací, jste přišli na to, že asi nejrychlejší bude použít instrukci PUSH, která během 11T uloží dva bajty a dekrementuje ukazatel zásobníku, a když trošku rozvinete cyklus, tak se dostanete na zhruba polovinu T proti tomu řešení s LDIR:

… a bylo to! (Uschování původní adresy SP jsem tu zanedbal)

Pokud vám nevadily změny příznakových registrů, tak jste nahradili LD A,0 (2 bajty, 7T) instrukcí XOR A (1 bajt, 4T)… atakdál. Náhodná čísla (spíš pseudonáhodná) jste mohli řešit třeba posloupností rotací a XORů s obsahem ROM (a třeba s registrem R). Algoritmus kreslení kruhu, který byl v BASICu řešen jako vykreslení obloukové úseče s úhlem 2PI, jste nahradili Bresenhamovým algoritmem, stejně tak čáru…

A jestli si pamatujete na Busysoftovo „MDA demo“, ve kterém běžel text v borderu, tak vězte, že byl vytvořený jako posloupnost instrukcí OUT (C),D a OUT (C), E – v jednom byla barva, ve druhém pozadí. Jedna čárka tak zabrala 12T, a aby bylo scrollování jemnější, byly před tím dvě instrukce NOP a podle fáze se skákalo buď na první (8 prázdných cyklů na začátku), druhý (4 prázdné cykly) nebo rovnou na OUT (0 prázdných cyklů). Změna obrazce se pak prováděla tak, že blok OUTů byl přepsán tak, aby se střídaly registry D a E podle znakové sady…

Takovéhle optimalizace byly fajn ještě do doby prvních šestnáctibitových procesorů. Pak přišel pipelining, který spoustu triků se samomodifikujícím se kódem „rozbil“ – a už se to nikdy nespravilo.

Dneska podobné optimalizace umí dělat ti maníci, co píšou překladače. Ti znají procesor „do posledního hradla“ a vědí, jak instrukce přeuspořádat tak, aby fungovaly co nejrychleji a aby pomohli procesoru v jeho spekulativních skocích a podobných skopičinách. Pokud nejste takovíhle master silicon nindžové, tak se rozlučte s myšlenkou, že ručně napíšete pro moderní procesor efektivnější kód, než jaký vytvoří optimalizující překladač.

Stejně tak se nevyplácí investovat moc času do optimalizací běžných aplikací, protože paměť i výkon jsou obrovské.

Jestli chcete ještě dneska někde hrát na bajty a cykly procesoru, musíte buď psát nízkoúrovňové ovladače, časově kritické úlohy, nebo embedded aplikace, tam se s podobnými výzvami ještě dneska setkáte.

Takže co zbylo z osmibitových triků pro dnešní použití? Vlastně nic moc, jen nějaké obecné techniky (double/triple buffering například), které nesouvisí až tak moc s procesorem.

Ale bohužel, pro vývoj webu, běžných aplikací, nedejbože Javovských aplikací, tyhle triky nepoužijete. Zato máte k dispozici jiné… Ale to je už jiná pohádka.

Příspěvek byl publikován v rubrice Software se štítky , , , , , . Můžete si uložit jeho odkaz mezi své oblíbené záložky.
  • Hynek Sladký

    Ono by to mohlo byt uzitecne i dnes, kdyby se kazdy (nebo aspon obcas nekdo) snazil psat takovy SW, ktery maximalne vyuziva moznosti HW – misto toho, aby byl uzivatel nuceny neustale HW upgradovat kvuli stale narocnejsim aplikacim, kvuli zvysujicimu se mnozstvi stahovanych dat pri nacteni uplne obycejne stranky…
    Pri cteni jsem si vzpomnel na nedavny clanek:
    http://m.zive.cz/co-dokaze-jedno-kilo-javascriptu/a-176255/?textart=1

  • Adam Štrauch

    Tak to se děje třeba na konzolích, kde s XBoxem 360 nebo PS3 dokázali vývojáři nemožné. Stačí se podívat na první hry a na hry, které vyšly třeba jen 5 let po uvedení konzole. Ale jak chceš optimalizovat na x86, kde jsou rozdíly i v procesorech jednoho výrobce? Na těch konzolích se budou používat ošklivé hacky, které ale hlavně seberou hrozně moc času. Menší studia si určitě rozmyslí, zda budou jednomu grafickému detailu věnovat 50 člověkohodin nebo ten čas věnují třeba testování nějaké herní mechaniky. Ale ani množství financí není samospásné, tyhle věci nejsou dostupné hned a ani tisícihlavý vývojový tým na nic rozumného v rozumný čas nemusí přijít.

  • Tom M.

    Přesně tak. PC hry žádnou takovou optimalizaci neuvidí ani z vlaku. Proto se vždy pohrdavě směju všem těm nerdům co si jakože skládají „herní PC“. PC je rakovina a sere mě už od dob 286 co se začali srát do Sinclair Clubu po revoluci.

  • Adam Štrauch

    Já si teda myslím, že díky PC jsme tam kde jsme. Nedokážu si představit, že by existovaly třeba jen dva modely PC, které by byly modernizované každých 5-10 let. Pokud zůstaneme u konzolí, tak už při vydání poslední generace konzolí se dalo koupit výkonnější PC a výstup z něj byl kvalitnější. Optimalizace jsou drahé a v některých případech umí být i ošklivé.

  • Díky, právě jste mě navedl na téma dalšího článku… 🙂

  • SPECZ

    Tyhle triky můžou být užitečné i dnes, jen ne na PC. Při psaní kódu pro nějaký mikrokontolér je to výzva, dostat z něj maximum, nebo naopak třeba co nejvíc setřit baterkou …

  • Ano, to se cudně skrývá za touhle větou: „Jestli chcete ještě dneska někde hrát na bajty a cykly procesoru, musíte buď psát nízkoúrovňové ovladače, časově kritické úlohy, nebo embedded aplikace, tam se s podobnými výzvami ještě dneska setkáte.“ 🙂