Monday, November 13, 2006

Let's Make It Faster...

Bu yazimda zaten hızlı olan bir seyi daha hızlı nasıl yapabiliriz onu gostericem :)

1. yol C dilinde asm komutları yazalım.
include

int main(){

int base, power ,result;

printf("us ve power degerleri:");
scanf("%d %d",&base,&power );

asm(
"movl %1, %%ebx\n\t" //base degiskeni ebx saklayıcısında
"movl %2, %%ecx\n\t" //power degiskeni ecx saklayıcısında"
"xorl %%edx, %%edx\n\t" //edx saklayıcısının içeri sıfırlandı çarpma işlemi için.
"movl $1, %%eax\n\t" //sonuc eax saklayıcısında tutulsun, baslangic degeri 1
"back:\n\t"
"cmp $0, %%ecx\n\t" //power == 0 mı ?
"je bitir\n\t" //evet ise dongu biter
"mul %%ebx\n\t" //degil ise base degiskeni ile sonucu carp ve sonuca ata
"decl %%ecx\n\t" //power ı 1 azalt
"jmp back\n" //donguye devam et, back e dallan
"bitir:\n\t" //dongu sonu

:"=a"(result) //result degiskeni eax saklayıcısından programa geri donecek
:"g"(base),"g"(power) //giris parametreleri base ve power degiskenleri
:"%ebx","%ecx","%edx" //asm blogunda degeri degisen saklayıcılar
);

printf("sonuc: %d\n",result);

return 0;
}


Program da goruldugu gibi hiz acisindan kritik olan kod parcasi asm blogu icinde assembly ile yazilmistir. Programci saglamsa cogu zaman derleyicinin urettigi koddan daha hizli olmaktadir:)
ornegin yukarıdaki kodun saf c karsiligi:

#include

int main(){

int base, power ,result;

printf("us ve power degerleri:");
scanf("%d %d",&base,&power );

result = 1;
for( ; power != 0; power--)
result = result * base;

printf("sonuc: %d\n",result);

return 0;
}

Ve bu fonksiyonu derleyip olusan object kodunu reassembler ettigimiz aman karsimiza gelen makine kodu:

lea ecx,[esp+4]
and esp,0xfffffff0
push DWORD PTR [ecx-4]
push ebp
mov ebp,esp
push ecx
sub esp,0x24
mov DWORD PTR [esp],0x0
call 19
lea eax,[ebp-16]
mov DWORD PTR [esp+8],eax
lea eax,[ebp-12]
mov DWORD PTR [esp+4],eax
mov DWORD PTR [esp],0x17
call 33
mov DWORD PTR [ebp-8],0x1
jmp 55
mov edx,DWORD PTR [ebp-12]
mov eax,DWORD PTR [ebp-8]
imul eax,edx
mov DWORD PTR [ebp-8],eax
mov eax,DWORD PTR [ebp-16]
sub eax,0x1
mov DWORD PTR [ebp-16],eax
mov eax,DWORD PTR [ebp-16]
test eax,eax
jne 40
mov eax,DWORD PTR [ebp-8]
mov DWORD PTR [esp+4],eax
mov DWORD PTR [esp],0x1d
call 6b
mov eax,0x0
add esp,0x24
pop ecx
pop ebp
lea esp,[ecx-4]
ret


Görüldüğü gibi makine kodunu benim yazdığım kodla karşılaştırmamız zor, çünkü ben c ve assemblyi karıştırdım, peki benim karıştırılmış kodumun object kodunu reassebler edersem ne görücem:

lea ecx,[esp+4]
and esp,0xfffffff0
push DWORD PTR [ecx-4]
push ebp
mov ebp,esp
push esi
push ebx
push ecx
sub esp,0x1c
mov DWORD PTR [esp],0x0
call 1b
lea eax,[ebp-24]
mov DWORD PTR [esp+8],eax
lea eax,[ebp-20]
mov DWORD PTR [esp+4],eax
mov DWORD PTR [esp],0x17
call 35
mov esi,DWORD PTR [ebp-20]
mov eax,DWORD PTR [ebp-24]
mov ebx,esi
mov ecx,eax
xor edx,edx
mov eax,0x1

0000004a :
cmp ecx,0x0
je 54
mul ebx
dec ecx
jmp 4a

00000054 :
mov DWORD PTR [ebp-16],eax
mov eax,DWORD PTR [ebp-16]
mov DWORD PTR [esp+4],eax
mov DWORD PTR [esp],0x1d
call 66
mov eax,0x0
add esp,0x1c
pop ecx
pop ebx
pop esi
pop ebp
lea esp,[ecx-4]
ret


anlaşılması kolay olsun diye satır numaraları ve byte code ları çıkarıp sadece assembly kodlarını koydum. Bu iki kodu karsilastirirsak gozuken o ki, benim yazdigim kod, derleyicinin urettigi koddan sadece 4 byte daha az :)) derleyicinin kodu 0x7c ye kadar giderken benim yazdığım kod 0x79 a kadar gitti. Aslında daha dikkatli asm bloğu ile bu farkı açabiliriz, ancak benim değinmek istedigim nokta iyi bir programcı daima compiler dan daha iyi kod yazabilir. Tabi bunu gerektirdigi noktalarda kullanmak gerekir. Tutupta butun kodu asm ile yazmak kotu bir yaklasimdir. Cunku bu adress binding dedigimiz, sembollerin adreslere dönüştürme işleminin erken gerçekleşmesine neden olur. Ancak hız kritik ve bellek kapasitesinin az olduğu uygulamalarda yukardaki yaklaşım önemlidir....

2.yol asm içinde C kutuphane fonksiyonlarının çağrılmasıdır. Bu konu ile ilgili örnekleri daha sonra verebilirim talep olursa:) ancak temek fikir ; çağrılacak C fonksiyonları asm kodunda extern olarak belirtilir. Böylece derleyici asm kodunu derlerken hata üretmez ve oluşturduğu object kodunun sembol tablosuna "imported symbol" olarak bu fonksiyon çağrısını ekler. Daha sonra linker da object kodunu C kutuphanesi ile link ederiz ve en son yürütülebilir dosya elimizde oluşur.
C fonksiyonlarını asm içinden çağırmak için gerekli olan bir önemli husus C nin calling conventions dedigimiz çağırma kurallarına uymamızdır. Parametre aktarımı yığın üzerinden olur ve fonksiyona aktarılacak parametreler, parametre listesine gore sagdan sola dogru yığına atılırlar.

Böylece bir yazımında sonuna gelmiş bulunmaktayım. Yeni yeni yazılarla karşınızda olmak dileğiyle şimdilik hoşçakalın :)))

No comments: