RSS

Wstawki asemblerowe straciły swą moc

Liczba odsłon: 50

W dawnych czasach laicy programowali w BASICu, amatorzy w Pascalu, a prawdziwi twardziele — w asemblerze. Nie było wyjścia: skompilowany kod wysokiego poziomu był dwa do trzech razy wolniejszy niż program napisany bezpośrednio w języku niskiego poziomu (nie mówiąc już o interpretowanym BASICu, wolniejszym dziesięć do dwudziestu razy).

Potem kompilatory poprawiły się, komputery przyspieszyły i nagle okazało się, że można całe programy pisać w języku wysokiego poziomu, w asemblerze zapisując jedynie wstawki wymagające interakcji ze sprzętem lub krytyczne czasowo. Programiści zyskali na wygodzie i efektywności, a programy nie straciły wiele na szybkości.

Dzisiaj nawet wstawki asemblerowe tracą sens. Napisałem ostatnio kilka procedur asemblerowych realizujących proste operacje na zmiennych łańcuchowych (badanie długości, wyszukiwanie znaków), porównując różne algorytmy ze sobą oraz z wbudowanymi funkcjami kompilatora gcc. Owszem, najlepsze algorytmy okazały się o 10-15% szybsze, niż natywne funkcje kompilatora — ale w niezoptymalizowanym programie.

Wyższe poziomy optymalizacji nakazują kompilatorowi wstawianie treści krótkich funkcji – takich, jak strlen() – bezpośrednio w miejscu wywołania. Choć powiększa to kod o kilkanaście bajtów na każde wywołanie, daje możliwość silnego związania funkcji z wywołującym ją kodem (a więc likwidację przesyłania parametrów), oszczędza dwóch skoków i składowania danych na stosie oraz zachowuje ciągłość biegu programu, oszczędzając pamięć podręczną. Efekt — tak zoptymalizowany program jest o 50% szybszy od najlepszej mojej procedury asemblerowej.

Owszem, można by tak zapisać wstawkę asemblerową, by również ulegała wstawieniu w miejscu wywołania. Problem w tym, że te silnie zoptymalizowane odmiany procedur są często znacznie dłuższe, niż ich prostsze odpowiedniki. Choć wywoływane niezależnie potrafią mimo tego być szybsze, w momencie włączenia w strumień kodu spowodowałyby znacznie większy jego rozrost i w efekcie działałyby wolniej, niż ich prymitywnie odmiany.


"tak zoptymalizowany program jest o 50% szybszy od najlepszej mojej procedury asemblerowej.”

Wszystko wskazuje na to że trzeba się jeszcze wiele nauczyć :-> :-P ;-) bo asembler ma to do siebie że gdy zależy nam na wydajnym kodzie to trzeba się sporo uczyć a potem jeszcze więcej czasu nabierać praktyki. Takie techniki optymalizacji jak rozwijanie pętli, unikanie skoków, czy odwołań do stosów były niezbędne już w latach 80-tych (np. procesory RISC zwykle nie przewidują wykorzystania stosu itp.).

Jak widać bycie autorem dobrych (szybkich) demek w asemblerze jest nie takie proste jakby się mogło wydawać...