Proč je programování v assembleru pitomost

12.01.2004 13:50

Proč je programování v assembleru pitomost

Právě prožíváme první dny roku 2004. To mimochodem znamená, že žijeme v 21.
století. Přesto mají pořád někteří lidé pocit, že programovat v assembleru je v
něčem dobré či positivní. Toto je absolutní omyl a já zkusím popsat proč.

Jediný důvod, který drží tyto lidi u tohoto přesvědčení je mýtus, že programy
napsané v assembleru jsou rychlé (no, možná ještě vrozený masochismus). Veškeré
ostatní aspekty programování (chybovost, pomalý vývoj, špatná udržovatelnost,
neskutečně pomalý tj. drahý vývoj, nepřenositelnost) v assembleru doufám všichni
uznávají jako negativní.

Programy napsané v assembleru nejsou rychlé.
Klasickým argumentem pro použití assembleru je rychlost provádění programu.
Panuje totiž všeobecné přesvědčení, že při psaní v assembleru je možné
\"napasovat\" program na konkrétní procesor a tím zabezpečit jeho rychlé vykonání.
Toto je mýtus. Dnešní procesory (dnešní znamená ty vyrobené za života myslím
velké většiny z nás) totiž při vykonávání posloupnosti instrukcí
movl %eax,%ebx
xorl %ebx,%ecx
jnz @loop
(toto jsem si náhodně vymyslel, nic v tom nehledejte) vůbec nevykoná co by
(neznalý) člověk myslel. No a vzhledem k tristní vzdělanosti je těch neznalých
většina. No, přejděme k věci - proč se vykoná něco jiného. V moderních (< 10
starých řekněme) procesorech (mluvím o CISCu a to např. i proto, že jinde než
na intelu jsem přímé programování v assembleru neviděl) funguje vykonávání
instrukcí takto: instrukce (její část určující typ) je vlastně jen adresou v
paměti mikrokódu. Takže vlastně když se vykonává \"movl %eax,%ebx\" tak se vykoná
několik tzv. mikroinstrukcí (dokonce existují architektury, kde se
mikroinstrukce skládají z tzv. nanoinstrukcí). To by ještě nevadilo, ale pro
zrychlení výkonu procesorů se v nich instrukce zpracovávají (neznám přesný
termín) zřetězeně (instructions are choped into small pieces which are fed to
pipeline(s) one by one allowing several instructions to be executed in
parallel). Tedy vykonání každé mikroinstrukce je rozloženo na několik fází s
tím, že když se mikroinstrukce A nachází ve fází 2, může vstoupit
mikroinstrukce B do fáze 1 (ie. začít se vykonávat). To by pořád ještě nevadilo
(myslím), co ale vadí je takzvané vykonávání mimo pořadí. Je logické, že aby se
mikroinstrukce (resp. instrukce) mohly vykonávat \"paralelně\" nesmí na sobě
záviset - proto se přeskupují. To je také důvodem proč se nevykoná (resp. ne
nutně) to co jste napsaly. Proto jsou snahy o zrychlování programů jejich
přepisem do assembleru zbytečné. Kód bude přinejlepším stejně rychlý jako ten
přeložený z vyššího jazyka. Situace je ale o něco horší, dokonce tak, že si
dovolím napsat:

Programy napsané v assembleru jsou pomalé.
I za předpokladu, že člověk při psaní v assembleru použije ty samé algoritmy
jako by použil ve vyšším jazyce (dost nereálné) jsou programy psané v
assembleru pomalé. Jde totiž o optimalizaci. V principu existují tři fáze
optimalizace kterýmy program při překladu projde. Optimalizace na úrovni
vyššího jazyka (ta při psaní v assembleru není). Optimalizace na úrovni
asembleru a konečně optimalizace v procesoru (přeskupování instrukcí, prefetch
apod.). Optimalizaci v procesoru asi steží ovlivníme a optimalizace na úrovni
vyššího jazyka není při psaní přímo v assembleru k dispozici (řekněme, že
dokážete provést jednoduché optimalizace - dead code, constant propagation
apod. ručně) a tak se podívejme na optimalizaci instrukcí. Normální člověk je
schopen myslet zaráz na cca 7 věcí (ta reklama tvrdící cosi o mužské
neschopnosti tohoto je pičovina). Pokud chceme ručně optimalizovat kód v
asembleru musíme si uvědomit následující faktory ovlivňující běh programu:
1) cache fit
1a) je třeba zarovnávat na velikost cache line apod.
1b) měl by být zachován princip lokality
2) optimální řazení instrukcí
2a) instrukce by měli používat jiné části procesoru
2b) instrukce by měli zpracovávat na sobě nezávislá data
2c) použití \"špinavých triků\" (NOP na vhodném místě apod.)
3) optimální výběr instrukcí
3a) CISCové procesory většinou umožnují pro jednu činost zvolit více
možností vykonání - je nutné zvolit optimální (kdo neviděl
xorl %eax,%eax místo movl $0,%eax)
3b) instrukce by měla neměla ovlivňovat nic jiného než své operandy
(vysvětlím dále)
4) samotný algoritmus (ideálně by měl zůstat funkční ;) )
možná ještě něco ale i tak je vidět, že je to na hranici možného. Jen stěží to
tedy dokážete lépe než program. Navíc jsou optimalizační kroky, které na úrovni
assembleru nemůžete použít. Zde vysvětlím bod 3b. V asembleru není jasná
struktura programu, co je vlastně smyslem kódu (to je zjevnější s vyšší úrovní
abstrakce - proto jsou vyšší jazyky lepší). V podstatě veškerá data (v
registrech o paměti ani nemluvě) jsou tedy brána jako nestálá. Toto slůvko jsem
zvolil záměrně. Anglicky se by se to zapsalo \"volatile\" - nepřipomíná vám to
něco? ;) (v jazyku C se takto označuje třída proměných o jejichž obsahu se nesmí
nic předpokládat - tj. neprovádí se na nich optimalizace). Všechno totiž může
být modifikováno v podstatě kdykoliv a čímkoliv. Další věcí je \"nezjevnost\"
některých věcí na úrovni asembleru a proto se nezoptimalizují.
int foo(unsigned short param)
{
signed short var = param;

if (var < 0) {
...
} else {
...
}

return (var & 0xff);
}
assembler nezná pojem typu a tak se v něm vykoná podmínka na zápornost
proměnné var (toto je zjevný příklad berte to principielně) a také vykonání
logického AND na konci (je zbytečné protože proměnná zůstane nezměnena). A to
ještě předpokládám, že existují optimalizující překladače asembleru (v životě
jsem žádný neviděl).

Jak vidno programování v assembleru je hloupost. Je škodlivé z mnoha důvodů
(špatné programátorské návyky, špatný kód, dlouhá doba vývoje atd.). Zdá se mi,
že jediný důvod proč se assembler používá, je pocit některých lidí, že když
dokáží přetrpět nesmyslnou dřinu programování v assembleru tak jsou dobří
(podobně jako lidé v armádě, většina manuálně pracujících apod.). Chápu, že u
většiny z nich by popření tohoto mýtu znamenalo ztrátu sebeúcty, protože takoví
lidé většinou nic (jiného, tj. užitečného) neumějí. ...and their extincting race
should be noticed. a last time... (Rudy Ratzinger paraphrasing F. Nietzsche)
Ale to se asi nestane. Hlavně se přestaňte zajímat o assembler (resp. jeho
strukturovanou podobu - jazyk C) a zkuste něco smysluplného.

neologism

P.S.1 Tento text jsem napsal abych si ujasnil některé teorie ke zkoušce z
\"Návrhu počítačových systémů\" (a abych si odpočinul). Tento předmět se mi dost
hnusí a vůbec mne nebaví- proto se nehodlám hádat ;-). Další věcí, která z toho
vyplývá je, že neručím za pravdivost/správnost zde podaných informací... ;

P.S.2 Jsem si vědom významu termínů \"assembler\", \"jazyk symbolických adres\",
\"strojový kód\". V tomto textu jsem, ale používal intuitivní významy. Tak si
prosím odpustťe komentáře na toto téma. Seru na ně. Stejně jako seru na
gramatyku.

P.S.3 Jo a vynechte kecy o bootovacích rutinách apod. Zapsání nějakých
hodnot někam není programování.neologism