2008-05-11

Co mnie boli w C, czyli to co mi daje Assembler, a nie ma tego w C.

Dzisiaj opiszę co jest moją bolączką w C. Co mnie denerwuje, ponieważ mam to w assemblerze, a w C już nie...

Pierwszą rzeczą której nie mogę wybaczyć C to brak stosu. Programowanie bez możliwości odrzucenia czegoś w każdej chwili, by parę funkcji dalej to ściągnąć, jest dla mnie nie wygodne. Co prawda, mogę zrobić kolejną zmienną, ale według mnie, nie jest to tak wygodne, jak wklepanie "push eax". Dlatego w niektórych programach, pisze swój, sztuczny stos. Zaletą, a jednocześnie wadą sztucznego stosu jest brak wywołań funkcji, w assemblerze zawsze mogło mi się odwidzieć wykonywanie danej funkcji i wrócić 3 funkcje wcześniej. Rzadko się przydaje i można użyć goto, chociaż "jmp" lepszy.

Druga rzecz której wręcz nie mogę podarować C, to brak systemu wykrywania przepełnień, który oferuje sam procesor x86! To co mam pod jednym mnenonikiem "jc" lub "jo" (zależnie czy przebicie ma być dla liczby ze znakiem, czy bez), muszę zastępować odzielną funkcją lub procedurą.

Trzecia rzecz, która dotkneła mnie w czasie własnej implementacji wielkich liczb, brak czegoś takiego jak mnemonik "adc". Jak on działa? Jeśli przy ostatnim dodawaniu, nastąpiło przeniesienie (więc 1 się nie zmieściło) to ma dodać ten 1 przy następnym dodawaniu. Jeśli użyjemy zwykłego add, +1 nie zostanie dodany. Kiedy to się przydaje? Kiedy działamy na liczbach większych niż największe naszego procesora. Dla przykładu, na liczbach 128bitowych. W czasach teraźniejszych można już użyć rozszerzenia procesora, ale dalej nie mamy 1KiB zmiennych (chociaż po co aż tak duża? Może jeśli chcemy wyrazić odległość ziemia słońce w cm? Myślę, że ta odległość jest mniejsza od 2**1024 cm).

Czy brak dostępu do niektórych rzeczy był wart większej przenośności? Odpowiedź na to pytanie, pozostawiam wam drodzy Czytelnicy.

2008-05-10

Czy programy napisane w assemblerze są lepsze od tych w C?

Słyszałem wiele opinni na ten temat. Ostatnio nawet ktoś wykłócał się co jest lepsze. O tym, że assebler nie może zostać zapomniany świadczy mój prosty test. Test został przeprowadzony na małym programie, wypisującym aktualny timestamp.

Na początek, wytłumaczenie dla nie informatycznych. Timestamp to ilość sekund, które mineły od 1 stycznia 1970 00:00:00 (tzw, początek ery uniksa, EPOCH) do chwili obecnej. Na podstawie tej wartości, oblicza się daty w komputerach z systemem operacyjnym z rodziny Unix, w Windows'ach jest chyba podobnie, ale u nich, czas liczony jest chyba od innej daty. Wiąże się to z pewnym problemem, ponieważ 19 stycznia 2038 03:14:07 czasu UTC, licznik się przekręci. Dobra, przejdżmy do wyników testów.

Pierw porównajmy wielkości programów. Na sam początek pójdzie statycznie skompilowany program wypisujący timestamp o kodzie zajmującym jedyne 91 bajtów:
#include<stdio.h>
#include<time.h>

int main(){
printf("%d\n", time(NULL));
return 0;
}

Kod myśle banalny, wypisz aktualny timestamp jako liczbę i wypisz znak nowej lini. A więc ten kod zajmuje 550959 bajtów, co daje w zaokrągleniu 551KB (538KiB). Teraz raczej unika się kompilowania statycznego, więc ten sam kod, skompilowałem dynamicznie, wynik od razu rzuca się w oczy, ponieważ jest to tylko 6399, co daje w zaokrągleniu 6KB (6KiB). Powalająca różnica, prawda? Teraz przyszedł czas na assemblera. Kod w assemblerze będzie "nieco dłuższy" ponieważ zajmuje 449kb, ale z pomocą kursu Pana Bogdana Drozdowskiego powstał naprawdę szybko. Oto kod:
global _start

section .text

_start:
mov eax, 0dh
xor ebx, ebx
int 80h
;przygotowanie do itoa
mov esi, 1
mov ecx, 10
itoa:
xor edx, edx
div ecx
or dl, '0'
mov [buff+esi], dl
inc esi
test eax, eax
jnz itoa

wypisz2:
mov ecx, buff
mov eax, 4
mov ebx, 1
add ecx, esi
mov edx, 1
int 80h
dec esi
jnz wypisz2

mov eax, 4
mov ebx, 1
mov ecx, buff
mov edx, 1
int 80h

exit:
mov eax, 1
int 80h
section .data
buff times 11 db 10

Kod nie jest maksymalnie wydajny, by nie powiedzieć, że wypisywanie zostało napisane na "odwal się". Tutaj proszę o uwagę, całość zajmuje jedyne 868 bajtów, czyli poniżej 1KB! Ale to nie jedyny test jaki przeprowadziłem, pora na...

...czasy wykonania programów! Czasy będę liczył za pomocą Linuksowego narzędzia time, więc można przyjąć, że będą wiarodajne, choć jeśli ktoś chce przeprowadzić własne testy, ma kody wyżej. Na pierwszy ogień idzie timestamp wykonany w C kompilowany statycznie. Po wielu wywołaniach programu, miałem wciąż te same wyniki:
real 0m0.019s
user 0m0.000s
sys 0m0.000s
więc uważam, że można im zaufać. Pierwszy wskaźnik pokazuje ile wykonywał się kod, 0.019 sekundy to naprawdę mało. Użytkownik nic nie wprowadzał, więc jego inwencja trwała zero i system uwinął się w poniżej 0.001 sekundy. Kolejny test, program w C skompilowany dynamicznie.
real 0m0.036s
user 0m0.000s
sys 0m0.000s
Jak widać, działał o 0.017 sekundy dłużej. Można sie było tego spodziewać, teraz kolej testu timestamp'u napisanego w assemblerze:
real 0m0.001s
user 0m0.000s
sys 0m0.004s
Tutaj widzimy system pracował dłużej, a jest to spowodowane wielokrotnym wywoływaniem przerwania wypisywania, ale po jednym znaku, C pierw wszystko formatowało, wrzucało do swojego buffora, a dopiero później wypisywało, przez co jądro miało mniej roboty, to samo mogłem zrobić w assemblerze, ale nie chciało mi się pisać dłużej kodu. Wyniki są jednoznaczne, program w assemblerze wykonał się w 0.005 sekundy.

Jeśli assembler jest taki mały i szybki, to dlaczego nie pisać w nim każdego programu? Assembler jest bardzo nie przenośny, algorytm napisany na PC, trzeba będzie przepisać, by można było go odpalić na mikrofalówce. C ma tutaj wielką przewagę, bo raz dobrze napisany algorytm, będzie działał na wielu platformach. Drugim argumentem przemawiającym przeciw assemblerowi, jest fakt że źródła programu napisanego w nim, są o wiele dłuższe niż w C(w naszym przypadku o okolo 80%). Istnieje też opinia, że w assemblerze się trudniej pisze, chociaż ja się z nią nie zgadzam, według mnie, jest on łatwiejszy, ale może to być złudzenie, ponieważ przez to że kody są w nim dłuższe, mogłem po prostu lepiej nauczyć się programować w tym języku.

Osąd, który z wyżej przedstawionych języków jest lepszy, pozostawiam wam, drodzy czytelnicy.

2008-05-05

Mała przerwa w blogowaniu.

Nastąpiła mała przerwa w blogowaniu, ale szczerze mówiąc nie wiem dlaczego. Może nie ma interesującego tematu? Dzisiaj zrobię reklamę innemu blogerowi, którego twórczość czytam.

Blog który zareklamuję to blog Charyzjusza Chakiera. Nie jest to blog z rodziny tych profesjonalnych, lecz blog, który jest parodią życia wielkiego fikcyjnego hakera, który nazywa się Charyzjusz Chakier. Polecam opowiadania publikowane na tym blogu, ponieważ są bardzo humorystyczne i według mnie, dobrze je się czyta przed lekturką polskiego bash'a.