1 Temmuz 2012 Pazar

Byte, Word ve Alignment Fault

Intel'in çığır açan 8080 ve 8086 işlemcileri hatta onun öncülü gelişmiş ALU 4004 işlemcisi piyasaya sürüldüğünde kimse olayın bu yazıları yazdığımız Pentium'lara varacağını tam olarak öngörememişti. O zamanlar henüz 4004'ler için bir nibble yani 4 bit bir word'u oluşturmak için yeterliydi. 8008 ve 8080'lerle birlikte word 8bit'e yani bir byte'a ve 8086 ile birlikte de bir word 16 bite çıktı.

Word yani CPU Word kelime anlamı olarak mikroişlemcinin bir kerede işleyebileceği en büyük veri miktarı. Yani bir 8086 işlemcinin bir word'unun 16 bit olması demek veri yolunun (data bus) 16 bit olması, yazmaçlarının (register) 16 bit olması ve bir işlemci çevriminde örneğin mov word ptr ax, [7F00h] komutuyla adresleme yoluna (address bus) 7F00h'yı sürmesi, MEMR bacağını set etmesi (daha doğrusu nMEMR'yi resetlemesi) ve veriyoluna o adresteki word'un okunarak o word içindeki bitlerin AX'e tutturulması anlamına geliyor. İşlemci 7F00h ve 7F01h'yı bir çevrimde okuyabiliyor. Ancak yine de mov byte ptr al,[7F01h] ile 7F01h adresindeki byte'a da erişebiliyorduk.

Zaman geçtikçe word kavramı kendi anlamını yitirdi yada byte kavramının altında kaldı demek daha doğru olur. 80386'lara eklenen 32 bitlik genişleme yazmaçlarla birlikte word 32bit'e çıkarıldı. Pentium'larda word hala 32 bitti ancak MMX'lerle birlikte garip bazı genişlemeler sayesinde word 64bitmiş gibi işlemler yaptırabiliyorduk. Veri yolu hala 32 bit olsa da CISC mimarisi sayesinde bellek okuma/yazma süreleri birden fazla işlemci çevrimine (cycle) dağıtılarak bir komutla 64 bitlik verilerin işlenebilmesi olanaklı oluyordu. Aslında bir bakıma word hala 32 bit olsa da 64 bitmiş gibi gösteriliyordu.

Önce nispeten yakın zamanda gelen x64 yada x86_64 mimarisiyle birlikte AMD gerçek anlamda word'u 64 bite çıkardı. Elbette adı üzerinde, bu mimariler varolan 32bitlik x86 mimarisine eklenen bir yama biçimindeki genişlemelerdi. Bana kalırsa artık geriye doğru uyumluluk kavramı uyumluluğun getirdiği avantajlar yanında sürekli eski komut setini desteklemenin getirdiği maliyeti kaldıramıyordu. Yani geriye doğru uyumluluğu yok sayarak geliştirilecek yenikomut setli yeni mimari hızlanmanın önünü açabilecekti. Bu da zaten IA64 yani Itanium mimarisine karşılık geliyor. x86_64'un hala masaüstü makinalarında olmasının nedeni bütün kodları yada en azından bütün derleyicileri bir anda IA64'e göre yazmanın zorluğundan kaynaklanıyor. Ancak yüksek performans piyasasında IA64 işlemciler şimdiden x86'ların yerini almaya başlıyor.

Peki neden bu kadar hardcore işlemci mimarisinden girdim? Çünkü taa 80386 zamanında işlemci word'u 32 bit olsa bile hala aldığımız RAM'leri megabayt üzerinden alıyorduk. Sene oldu 2012 ama hala RAM'ler word üzerinden değil de byte üzerinden satılıyor. Aslında aldığımız 32MB RAM işlemci için boş 8Megaword'luk boş alan içerdiğini düşününce insan biraz kazıklanmış hissetmiyor değil. Diğer taraftan hemen yukarıda 7F01h adresindeki byte'a 8086'larda iki türlü erişilebileceğinden bahsetmiştim. Adreslemeyi byte byte yaptığımız halde bir anda 2 byte'a birden erişmek Intel mühendislerine biraz gereksiz gelmiş olacak ki bellek erişimini hızlandırmak adına yanılmıyorsam Pentium'lardan itibaren adresleme veri yolunun en düşük anlamlı 3 bitini iptal ettiler. Yani Pentium'larda A0, A1 ve A2 artık bulunmuyordu. Sizin 8086'nızda çalıştırdığınız masum mov byte ptr al, [7F01h] komutu Pentium'larla birlikte artık mov dword ptr eax, [7F00h] olarak çalışıyor ve sonrasında gereksiz kısımlar atılarak gereken kısımlar AL'ye yazılıyordu. Bu arada dword yani "double word"ün altını çizmek lazım. Seneler içerisinde word kavramı da karışıklık olmasın diye 16bit'te sabitlenmiş ve 32bitlik word, double word olarak adlandırılmaya başlanmıştı. 64bit'eyse qword diyecektik. Tabi genişletilmiş adresleme yolu sayesinde korumalı modda 00007F00h adresini kullanıyorduk.

Buraya kadar herşey aslında normal gibi ancak zurnanın zırt dediği yer işlemciye mov dword ptr eax, [7F01h] verdiğimiz zaman ortaya çıkıyor. Bu durumda işlemci şunu yapmaya çalışacak:






Ancak bunu yaparken artık 7F01h'ya doğrudan erişemiyor. Dolayısıyla 7F00h'dan okuyup bunu EAX'in gerekli kısımlarına yazacak. Sonra diğer çevrimde 7F04h'ya erişip oradan gerekli olan verileri okuyacak ve yine EAX'in gereken kısımlarına yazacak. Dolayısıyla normalde bir çevrimde tamamlanması gereken okuma iki çevrimde tamamlanacak. Elbette korumalı modda araya girecek denetlemeleri ve kesmeleri saymıyorum. Ancak erişim 4ün katı olan yada başka bir deyişle sonunda üç tane sıfır olan bellek adresine yapılacak olsaydı tek bir çevrimde okuma yada yazma olanaklı olacaktı. Bu da performansı düşüren bir durum. Günümüzde derleyiciler size sormaya gerek bile duymadan bellekte ayırdığınız bütün verileri dördün katlarına gelecek biçimde hizalarlar. Zaten bu olaya hizalama denmesinin nedeni de bu.

Intel mühendisleri bununla da kalmadılar. Eğer EFLAGS'ın AC (Alignment Check) ve CR0'ın AM (Alignment Mask) bitlerini set ettiğiniz zaman size Alignment Fault adındaki istisnayı (exception) atarlar. 80286'nın exception belgelerine baktığınız zaman alıştığımız sıfıra bölme, tuzak kapısı, NMI vb. haricinde 8-15 arasını ayrılmış olarak görüyoruz. 16 yani 10h'dan sonrasını BIOS Video, Disk vb. kesme hizmet programlarının olduğunu biliyoruz. Ancak daha sonradan eklenen bu Alignment Fault kesmesi 17 yani 11h'yı kullanıyor. Bu durum biraz kafa karıştırıcı.

İşletim sistemleri performans açısından bu kesmeye asılıp performans denetlemesi yapabilir ve bu şekilde kullanıcıyı uyarabilir hatta mavi ekran bile çıkartabilirler. Daha önce de dediğim gibi normalde basit bir malloc() fonksiyonu çağırıldığında işletim sistemi dördün katı olan bir adres sağlar. Derleyici de ayırılan dizinin elemanlarını dördün katlarında bulunacak biçimde adreslemeye çalışır. Yine de bazen derleyicinin yapabileceği bir şey kalmayabilir: Örneğin 5 byte'lık bir struct tanımladıktan sonra bu struct üzerinden malloc() çağırırsanız. Böyle durumlarda icc kullanarak derleme yaptığınız zaman UNIX sistemler kullanıcıyı program çalışması esnasında uyarıyorlar.

Son olarak bütün bunları bir yere bağlamak gerekirse; SuperLU kütüphanesini hem x86_64 mimarili Intel Xeon işlemcide hem de Itanium2 işlemcili makinalarda derleyip çalıştırdım. Her ikiside sorunsuz derlendiği halde kurulum testlerinde Itanium makinada hizalama uyarısı verdi:

pstest(17541): unaligned access to 0x600000000003733c, ip=0x4000000000099230

Dikkat edilirse erişilen adres dördün katı ancak erişim yapan makina 64bit. Yani erişilen adresin sekizin katı olması gerekiyor. Aynı hatayı x86_64 makinada vermiyor. Üstelik büyük boyutlu denemelerde programın çalışma süresi yaklaşık dörtte bir daha uzun sürüyor. Programları 64bit makinalar için yazarken, araştırınca Intel icc için basit derleyici komutlarıyla nasıl qword'luk hizalama yaptırabileceğinizi belirtiyor ancak sözkonusu program SuperLU gibi büyük ve başkasının yazdığı bir program olduğunda düzeltme yapmak can sıkıcı uzunlukta olabilir.


Hiç yorum yok:

Yorum Gönder