Czy istnieje assembler PASM?

ByTomasz Sanecki

Czy istnieje assembler PASM?

W praktyce, gdy mówi się o PASM, chodzi zwykle o podejście do programowania z wykorzystaniem składni i narzędzi związanych z assemblerem, a nie o „magiczny” osobny język kompilowany inaczej niż inne warianty asemblera. W ostatnich latach kompilatory oraz algorytmy coraz częściej potrafią dawać duże przyspieszenia, ale nadal zdarza się, że znaczną przewagę prędkości nad odpowiednikami napisanymi w wyższych językach uzyskuje się właśnie w asemblerze.

Assember jest przy tym bardzo blisko kodu maszynowego: jest 1:1 odzwierciedleniem kodu maszynowego (tego kodu zjadzanego przez CPU), więc tłumaczenie kodu maszynowego na asembler i w drugą stronę jest proste i prawie jednoznaczne. To dlatego każdy statycznie skompilowany kod da się zapisać jako kod asemblerowy.

Co daje programowanie w assemblerze i dlaczego czasem wciąż ma sens

Najsilniejsze obszary, w których assembler potrafi dawać przewagę, to:

  • operacje logiczne i arytmetyczne (np. typ unsigned int pod C),
  • użycie specjalnych rejestrów, konkretnie ESP i EBP,
  • pełna kontrola nad tym, co dzieje się z pamięcią i danymi w programie,
  • możliwość ręcznego dopasowania instrukcji pod możliwości procesora.

Możliwości te dotyczą również typowych problemów niskopoziomowych: pamięć zarezerwowana na potrzeby programu jest wykorzystywana m.in. są na stosie. Jest wartość. W takim modelu wyraźnie widać, że kod w assemblerze to nie „ładne wstawki”, tylko sposób myślenia o tym, jak działa wykonanie programu.

Ważnym ograniczeniem bywa środowisko: ogranicza nas system, w naszym wypadku Windows. Można się wtedy natknąć na efekty typu zwis (np. kończy się potocznie zwanym zwisem), a więc błędy w pracy ze stosem i wywołaniami funkcji mogą być bardzo kosztowne.

Stos, wywołania funkcji i ręczne korygowanie

Assember daje pełną kontrolę, ale też wymusza dyscyplinę. Gdy program wywołuje funkcję, oczekuje się często, że stan będzie poprawny po powrocie. Jednak samo korygowanie stosu musi być wykonane ręcznie.

Jeśli zapisujesz/masz w kodzie dane, które mają pozostać zachowane, to wyjście z funkcji oczekiwane jest, że nadal będzie wyzerowana. Stosu ESP był za każdym razem korygowany. Najprawdopodobniej wprowadzony, ponieważ… (w tego typu rozważaniach właśnie wychodzi, jak działa ręcznie kontrolowany stos).

W praktyce oznacza to, że łączenie „logiki aplikacji” z kontrolą ramek wywołań i stosu trzeba robić świadomie, bo to nie jest automatycznie załatwiane jak w standardowym modelu wyższych języków.

SIMD, MMX i przewaga wydajności

W dalszej części pojawia się wątek jednostki MMX i przetwarzania multimedialnych danych: kod MMX. Wykorzystujące kod MMX. (oprócz specjalizowanych takich jak np. kodu, wpływ na to, ma fakt wykorzystania ww. trybu SIMD. wydajniejsze.)

W takim układzie przewaga może być ogromna w specyficznych pętlach i algorytmach, gdzie liczą się powtarzalne operacje na danych. Na przykład efekt zanikania ekranu (tzw. rozmazywanie obrazu (tzw.) oraz pętle przetwarzające obrazy mogą być obszarem, gdzie użyjesz napisanego kodu MMX.

Wprost pada też przykład: użyjemy np. napisanego kodu MMX, który jest trzykrotnie szybszy od tego konkurencji. Takie wyniki nie muszą dotyczyć każdego programu, ale pokazują, że assembler bywa narzędziem do „wyciśnięcia” konkretnego fragmentu wydajności.

Dlaczego assembler nie jest tylko wstawkami

Można spotkać podejście, w którym asembler jest użyty do napisania tylko części programu, ale równie ważny jest drugi aspekt: pisanie w asemblerze to nie tylko wstawki i pojedyncze procedury.

że nie można napisać dużej aplikacji w assemblerze od podstaw. aplikacje. problem przestaje być problemem. ok. 90kb pliku wykonywalnego. zawierająca 1 okno, po skompilowaniu zajmuje ok. 6. (w kontekście tego typu rozmiarów chodzi o to, że można zbudować typową aplikację w bardzo zwartej formie).

To prowadzi do ogólnej obserwacji: kod asemblera potrafi dawać bardzo małe pliki i pełną kontrolę nad tym, co znajduje się w wyniku.

Czy to samo da się robić w językach wysokiego poziomu?

Żaden język wysokiego poziomu nie daje tej samej, bezpośredniej relacji z instrukcjami procesora. Języki wysokiego poziomu są optymalizowane przez kompilator, więc tutaj szybkość zależy od jakości optymalizacji wbudowanych w procesor. Asembler jest tłumaczony wprost, więc tutaj optymalizuje się ręcznie.

Przewagą asemblera jest słabe wsparcie kompilatorów dla instrukcji SIMD czy też innych mało używanych instrukcji (np prefetching danych). To szczególnie istotne, jeśli masz bardzo specyficzny kod, gdzie chcesz sterować instrukcjami i kolejnością przetwarzania danych.

Oczywiście wciąż pozostaje fakt, że można łączyć asemblera z jezykami wyższego poziomu.

Dwa sposoby wykorzystania kodu asemblera

Możesz wykorzystać assembler na dwa sposoby. Pierwszym z nich są tzw. wykorzystanie kodu asemblera na dwa sposoby, pierwszym z nich są tzw. napisanym np. w C++. W której zapisuje się kod asemblera. Pierwszym typem jest tzw. standard i 90% źródeł zapisanych jest zgodnie z nią.

Assember ma składnię „at&t” wykorzystywana np. // np. // np. // np. // np. // np. // np. // np. // np. Praktycznie ma się takie same możliwości jak zwykły kod.

Można też traktować to jak typowe wbudowanie asm-u w kod programu, gdzie ważne jest, że ma być taki sam jak przed wywołaniem funkcji). (Language - język wysokiego poziomu) oraz ww. programu do trzymania np. adresów jakichś funkcji, stałych wartości itp. całej aplikacji. po wyjściu z funkcji oczekiwane jest, że nadal będzie wyzerowana.

Drugim sposobem jest niezależne budowanie modułów: kod asemblerowy można kompilować do plików .o czy .obj (zależnie co tam kompilator wyrzuca), a potem łączyć linkerem z plikami powstałymi z kodu C++owego.

Dank temu można np. mieć 99 % programu w C++ i 1 % programu w asmie.

Przykłady zastosowań i narzędziowe konteksty

W kwestii zastosowań przewija się kompresja: wykorzystywany do pisania specyficznych programów jak np. skompresowaniu go UPXem jego rozmiar zmniejszy się do ok. kompresją. W tym wypadku asembler jest wykorzystany do napisania kodu tzw. powoduje jego uruchomienie.

Jednocześnie warto pamiętać o ograniczeniu czytelności: Wadą asemblera jest mała czytelność kodu (w przypadku skomplikowanych obliczeń: tragiczna) i brak natywnych (tzn napisanych w asemblerze) bibliotek.

Z drugiej strony, debuggery pokazują kod asemblerowy przy kodzie, do którego nie ma źródeł (wysokiego poziomu). To jest mocna przewaga diagnostyczna: łatwo przejść z zachowania programu do jego realnego zestawu instrukcji.

Jeszcze jedna obserwacja dotyczy wielkości i kontroli: Programy są z reguły mniejsze, bo masz zwykle 100 %ową kontrolę nad plikiem wynikowym i tym co on zawiera.

Assembler a wydajność: od talentu do instrukcji

Szybkość zależy od talentu programisty. Języki wysokiego poziomu są optymalizowane przez kompilator, więc tutaj szybkość zależy od jakości optymalizacji wbudowanych w procesor. Asembler jest tłumaczony wprost, więc tutaj optymalizuje się ręcznie.

W pewnym sensie to sprowadza się do tego, że jeśli potrafisz wykorzystać rzadziej spotykane instrukcje i umiesz sterować kodem na poziomie instrukcji procesora, to masz szansę uzyskać przewagę: przewagą asemblera jest słabe wsparcie kompilatorów dla instrukcji SIMD czy też innych mało używanych instrukcji (np prefetching danych).

Ostatecznie odpowiedź na pytanie „czy istnieje assembler PASM?” w praktycznym sensie zależy od tego, co dokładnie kryje się pod tą nazwą. Jednak w kontekście samej idei programowania w assemblerze i uzyskiwania korzyści wydajnościowych, materiał pokazuje jasno: kluczowa jest relacja kodu z instrukcjami CPU, kontrola stosu i rejestrów oraz świadome użycie instrukcji (np. SIMD/MMX).

Na upartego nawet z Javą poprzez interfejsy JNI, ale bardziej powszechne jest łączenie z C++.

schemat wywołania funkcji i stos
SIMD MMX porównanie szybkości

Porównanie: gdzie asembler może wygrywać

Obszar Jak pomaga assembler
Instrukcje niskopoziomowe Asembler jest tłumaczony wprost, więc optymalizuje się ręcznie.
SIMD / MMX Lepsze dopasowanie do instrukcji i pętli przetwarzających dane multimedialne.
Mało popularne instrukcje Przewagą asemblera jest słabe wsparcie kompilatorów dla takich instrukcji.
Kontrola nad plikiem wynikowym Masz zwykle 100 %ową kontrolę nad plikiem wynikowym i tym co on zawiera.
Czytelność i biblioteki Wadą jest mała czytelność kodu i brak natywnych bibliotek.

tags: #czy #jest #asembler #pasm

Popularne posty:

About the author

Tomasz Sanecki administrator

Informatyk z zawodu i zamiłowania. Od 2007r właściciel firmy Perfect Systems. Specjalista do spraw bezpieczeństwa IT, serwisu urządzeń elektronicznych, sieci komputerowych. Prywatnie mąż i ojciec dwójki dzieci. Pasjonat nowoczesnych technologii, motocykli oraz włoskiego espresso.