Ders 08 / 16
Yazmaçlar ve Ana Bellek
Belleğin adreslenen bir dizi olarak modeli, yazmaçların rolü ve erişim maliyeti farkı.
İçindekiler
Önceki konu, verinin bit örüntüsü olarak nasıl temsil edildiğini kurdu. Bu bit örüntüleri bir yerde durmak zorundadır ve durdukları yer, program başarımını doğrudan belirler. Bu konu, o yerin yapısını ve verinin işlemciye nasıl taşındığını ele alır.
Bu dersin sorusu şudur: bellek bir programa nasıl görünür ve işlemci üzerinde işlem yapacağı veriyi nerede tutar?
Bellek: Adreslenen Bir Dizi
Programın gördüğü bellek modeli sadedir: her biri bir bayt tutan, sıfırdan başlayarak numaralanmış hücrelerden oluşan bir dizi. Hücrenin numarasına adres, içeriğine değer denir.
| Adres | 1000 | 1001 | 1002 | 1003 | 1004 |
|---|---|---|---|---|---|
| Değer | 41 |
42 |
43 |
44 |
00 |
Bu modelin iki sonucu vardır. Birincisi, belleğe erişim iki bilgi gerektirir: hangi
adres ve kaç bayt. Önceki konudaki 0x41424344 değeri dört bayttır; bu değeri okumak,
1000 adresinden başlayarak dört bayt istemek demektir. Kaç baytın hangi sırayla
yorumlanacağı, bayt sırası dersinde tanımlanan kuraldır.
İkincisi, adresin kendisi de bir sayıdır ve bellekte saklanabilir. Bir değişkenin adresini tutan değişken — yani işaretçi (pointer) — bu gözlemin doğrudan sonucudur. Adres genişliği, adreslenebilecek en büyük bellek miktarını belirler: 32 bitlik adresler farklı bayt, yani yaklaşık dört milyar hücre adresleyebilir; 64 bitlik adreslerin sınırı pratikte hiçbir donanımın ulaşamayacağı büyüklüktedir.
Yazmaçlar
İşlemci, üzerinde işlem yapacağı değerleri ana bellekte tutmaz. Aritmetik ve mantık birimi yalnızca kendi içindeki küçük ve hızlı saklama birimlerinden okuyabilir; bunlara yazmaç (register) denir.
Yazmaçların üç ayırt edici özelliği vardır:
- Sayıları azdır. Bir işlemcide genel amaçlı yazmaç sayısı onlarla ifade edilir; ana bellekteki hücre sayısı milyarlarla.
- Adresleri yoktur. Yazmaçlara numarayla değil, komutta doğrudan adlarıyla atıf yapılır.
- Erişimleri neredeyse ücretsizdir. Yazmaç okuma, işlemcinin bir saat çevrimi mertebesindedir.
Bazı yazmaçlar özel görevlidir: program sayacı bir sonraki komutun adresini, yığıt işaretçisi çağrı yığıtının tepesini tutar. Bunların işlevi sonraki derslerde tanımlanacaktır.
Bir toplama işlemi bu nedenle tek adım değildir. a + b hesabı için önce iki değer
bellekten yazmaçlara yüklenir, toplama yazmaçlar arasında yapılır, sonuç yeniden
belleğe yazılır. Yükle–işle–sakla düzeni, işlemci mimarilerinin ortak iskeletidir.
Erişim Maliyeti Farkı
Yazmaç ile ana bellek arasındaki hız farkı, program tasarımını etkileyecek büyüklüktedir. Kesin süreler donanıma göre değişir; değişmeyen, mertebe farkıdır:
| Katman | Erişim mertebesi (saat çevrimi) |
|---|---|
| Yazmaç | ~1 |
| Birinci düzey önbellek | birkaç |
| Son düzey önbellek | onlarca |
| Ana bellek | yüzlerce |
| Kalıcı depolama | on binlerce ve üzeri |
Her satır, bir öncekinden belirgin biçimde yavaş ve belirgin biçimde büyüktür. Bu kademelenmeye bellek hiyerarşisi denir ve tasarımının nedeni ekonomiktir: hızlı saklama pahalıdır, ucuz saklama yavaştır. Hiyerarşi, ikisinin arasında bir denge kurar.
Sonuç şudur: bir programın hızı, yaptığı işlem sayısı kadar, veriye nereden eriştiğine de bağlıdır. Ana bellekten okunan tek bir değer, yüzlerce aritmetik işlem kadar süre alabilir. Bu gözlem, sonraki derste ele alınacak önbelleğin var oluş nedenidir.
Saklama Ortamlarının Farkı
Hiyerarşinin katmanları yalnızca hız ve kapasiteyle değil, fiziksel çalışma biçimleriyle de ayrılır.
Yazmaçlar ve önbellek, durumu besleme gerilimi sürdüğü sürece koruyan devrelerle kurulur; erişim hızlıdır, birim başına maliyet yüksektir. Ana bellek ise durumu küçük yüklerde tutar ve bu yükler zamanla boşaldığı için düzenli olarak tazelenmek zorundadır. Tazeleme, ana belleğin hem daha yavaş hem de daha ucuz olmasının nedenidir: hücre başına daha az devre öğesi kullanılır.
Bu üç katmanın ortak özelliği uçuculuktur: güç kesildiğinde içerik kaybolur. Kalıcı depolama ise içeriği güç olmadan da korur, ancak erişimi mertebelerce yavaştır ve yazma işlemleri genellikle blok düzeyinde yapılır.
Uçuculuk ayrımı, bir programın çalışırken tuttuğu verinin neden ayrıca kaydedilmesi gerektiğini açıklar: bellekteki durum, sürecin ömrüyle sınırlıdır.
Sözcük Genişliği ve Hizalama
Sözcük (word), işlemcinin doğal olarak işlediği veri genişliğidir; genel amaçlı yazmaçların genişliğiyle örtüşür. 64 bitlik bir işlemcide sözcük sekiz bayttır.
Donanım, çok baytlı erişimleri sözcük sınırlarına göre yapar. Bu nedenle veri
yapılarının bellekte yerleşimi hizalama (alignment) kuralına uyar: baytlık bir
değerin adresi, genellikle ’in katı olacak biçimde seçilir. Dört baytlık bir tam
sayı 1000 adresine yerleşir; 1001 adresine değil.
Hizalanmamış erişimin bedeli donanıma göre değişir: kimi işlemciler böyle bir erişimi iki bellek işlemine bölerek yavaşlatır, kimileri hata üretir.
Hizalama kuralının görünür sonucu dolgudur (padding). Bir yapı içinde küçük ve büyük alanlar ardışık tanımlandığında derleyici araya kullanılmayan baytlar ekler:
struct A struct B 1 baytlık alan 8 baytlık alan 7 bayt dolgu 1 baytlık alan 8 baytlık alan 7 bayt dolgu toplam: 16 bayt toplam: 16 bayt
Aynı alanları farklı sırayla tanımlamak, yapının bellekteki boyutunu değiştirebilir. Alanları genişliğe göre azalan sırada dizmek, dolguyu en aza indiren yaygın bir alışkanlıktır. Bu ayrıntı, bir önceki derste ham bellek kopyasının neden taşınabilir olmadığını da açıklar: dolgu miktarı derleyiciye ve donanıma bağlıdır.
Bellek Yerleşimini Görmek
Aşağıdaki program, aynı verinin farklı gösterimlerini ve bir nesnenin bellekte kapladığı yeri inceler:
import sys, array deger = 0x41424344 # Belleğe yazılacak dört bayt (büyük uçlu düzende) ham = deger.to_bytes(4, "big") for ofset, bayt in enumerate(ham): print(f"adres+{ofset}: {bayt:#04x}") # 0x41 0x42 0x43 0x44 # Sabit genişlikli diziler, elemanları bitişik ve hizalı tutar. dizi = array.array("I", [1, 2, 3, 4]) # 'I': 4 baytlık işaretsiz tam sayı print(dizi.itemsize, len(dizi)) # 4 4 print(dizi.buffer_info()[1] * dizi.itemsize) # 16 — toplam bayt # Python nesneleri ham veriden çok daha fazla yer tutar. print(sys.getsizeof(1), sys.getsizeof(dizi)) # nesne başlığı dahil boyutlar
array modülü, elemanları sabit genişlikte ve bitişik saklar; bu, veri yapılarının
donanım dostu yerleşimine en yakın Python karşılığıdır. Buna karşılık genel amaçlı bir
liste, elemanlarına işaretçiler tutar: değerlerin kendisi bellekte dağınık durur. İki
yerleşim arasındaki fark, sonraki derste ele alınacak önbellek davranışında görünür
hâle gelir.
Özet
- Bellek, programın gözünde adreslenmiş bayt hücrelerinden oluşan bir dizidir; erişim hem adres hem genişlik gerektirir.
- Adres de bir sayıdır ve bellekte saklanabilir; işaretçi kavramı bu gözlemden çıkar.
- İşlemci yalnızca yazmaçlar üzerinde işlem yapar; hesap, yükle–işle–sakla düzeninde yürür.
- Yazmaç, önbellek, ana bellek ve kalıcı depolama arasında mertebe düzeyinde hız farkı vardır; bu kademelenme bellek hiyerarşisidir.
- Çok baytlı değerler hizalama kuralına göre yerleşir; kural, yapılarda dolgu baytlarına ve alan sırasına bağlı boyut farklarına yol açar.
Sonraki Adım
Verinin nerede durduğu tanımlandı. Komutların kendisi de bellekte durur ve aynı adres
uzayını paylaşır. Sonraki ders, işlemcinin bir komutu bellekten alıp yürütme döngüsünü
ve bu döngünün program akışını nasıl oluşturduğunu ele alacak; orada 0x41424344
örüntüsü, bu kez bir komut olarak okunacak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.