Modern bilgisayar mimarisinde, önbellek (cache) bellek, işlemci ile ana bellek (RAM) arasında konumlandırılmış, düşük gecikmeli ve yüksek hızlı bir veri depolama katmanı olarak tanımlanır. Temel işlevi, sıkça erişilen veri ve talimatların ana bellekten çok daha hızlı erişilebilir bir ortamda tutularak, genel sistem performansının ve veri aktarım hızının (throughput) kritik ölçüde artırılmasıdır. Bu mimari yaklaşım, işlemci ve bellek arasındaki hız farkı olan “duvar” (memory wall) problemini hafifletmek için tasarlanmıştır.
Önbellek mekanizmasının performansı genellikle erişim süresi (hit time), ıska maliyeti (miss penalty) ve en önemlisi bulma oranı (hit rate) ile ölçülür. Bir isteğin önbellekte bulunması durumuna hit, bulunamaması durumuna ise miss adı verilir. Bulma oranı, toplam erişimler içinde "hit" olan erişimlerin yüzdesidir. Yüksek bir bulma oranı, sistemin büyük ölçüde yüksek hızlı bellekten faydalandığını gösterir.
Önbelleğin organizasyonel yapısı, verimliliği doğrudan etkiler. Bu yapı, doğrudan eşlemeli (direct-mapped), küme ilişkili (set-associative) ve tam ilişkili (fully associative) olmak üzere üç ana kategoride incelenir. Her bir model, adres eşleme (address mapping), blok yer değiştirme (block replacement) ve yazma politikası (write policy) konularında farklı karmaşıklık ve performans trade-off'ları sunar.
Önbellek performansının teorik bir analizi, ortalama bellek erişim süresi (Average Memory Access Time - AMAT) formülü ile yapılır: AMAT = Hit Time + (Miss Rate * Miss Penalty). Bu formül, önbellek tasarımının temel optimizasyon hedeflerini açıkça ortaya koyar: erişim süresini ve ıska oranını minimize etmek. Pratikte bu hedefler genellikle birbirleriyle çelişir, bu da tasarmda bir denge gerektirir.
- Önbellek (Cache): Ana belleğin bir alt kümesini geçici olarak saklayan hızlı bellek birimi.
- Bulma Oranı (Hit Rate): İstenen verinin önbellekte bulunma yüzdesi.
- Iska Oranı (Miss Rate): 1 - Hit Rate. İstenen verinin önbellekte bulunamama yüzdesi.
- Ortalama Erişim Süresi (AMAT): Bir bellek isteğini tamamlamak için gereken ortalama süre.
Önbellek Türleri ve Seviyeleri
Önbellek sistemleri, kapasite, gecikme süresi ve fiziksel yakınlıklarına göre hiyerarşik bir yapıda düzenlenir. Bu hiyerarşi, L1 (Seviye 1), L2 (Seviye 2) ve L3 (Seviye 3) olarak adlandırılan, giderek büyüyen kapasiteye ancak artan gecikme süresine sahip katmanlardan oluşur. L1 önbellek genellikle işlemci çekirdeğinin içine gömülüdür, en hızlısıdır ve talimat (L1i) ve veri (L1d) önbelleği olarak ayrılabilir. Bu ayrım, Harvard mimarisinin bir uzantısı olarak, aynı anda talimat getirme ve veri erişimi işlemlerine izin vererek verimi artırır.
L2 önbellek, L1'e kıyasla daha büyük kapasiteye sahiptir ve genellikle çekirdek başına özel veya birkaç çekirdek tarafından paylaşılan olarak uygulanır. L3 önbellek ise genellikle tüm işlemci çekirdekleri, entegre grafik işlemci (GPU) ve diğer aracılar tarafından paylaşılan, büyük bir kapasiteye sahip son seviye önbellektir (Last-Level Cache - LLC). Bu hiyerarşide, bir üst seviyedeki ıska (miss), bir alt seviyede araştırmayı tetikler. İlk olarak L1'de aranan bir veri bulunamazsa (L1 miss), L2'ye bakılır; orada da bulunamazsa (L2 miss), sırasıyla L3 ve ana bellek kontrol edilir. Her seviye, daha düşük ıska oranı sağlamak için daha büyük kapasite sunar, ancak bu, daha yüksek gecikme ve daha yüksek enerji tüketimi pahasına gerçekleşir.
Önbellek türleri sadece hiyerarşik seviyelerle sınırlı değildir. Yazılım ve özel donanım ihtiyaçlarına bağlı olarak çeşitli özel önbellekler de mevcuttur. Çeviri Yönlendirici Tamponu (TLB), sanal adreslerin fiziksel adreslere çevrilmiş hallerini önbelleğe alarak bellek yönetim biriminin (MMU) performansını artırır. Disk önbelleği (page cache), işletim sistemi tarafından sık erişilen disk bloklarını RAM'de tutmak için kullanılır. Tarayıcılar ve web sunucuları ise, ağ gecikmesini azaltmak için web önbelleği kullanır.
| Önbellek Seviyesi | Konum | Gecikme (Cycles) | Kapasite (Tipik) | Birincil Amaç |
|---|---|---|---|---|
| L1 (Talimat/Veri) | İşlemci Çekirdeği İçi | 1-4 | 32-64 KB / Çekirdek | En hızlı erişim, çekirdek ihtiyaçları |
| L2 | Çekirdek Yakını / Paylaşımlı | 10-25 | 256 KB - 1 MB / Çekirdek | L1 ıskalarını yakalama |
| L3 (LLC) | Çok Çekirdekli İşlemci İçi, Paylaşımlı | 30-50+ | 8-64 MB / İşlemci | Çekirdekler arası veri paylaşımı, L2 ıskalarını yakalama |
Modern işlemcilerde, özellikle çok çekirdekli mimarilerde, önbellek tutarlılığını sağlamak ve paylaşımlı veri erişimini yönetmek için son seviye önbelleğin (LLC) rolü kritiktir. LLC, farklı çekirdeklerin aynı veri üzerinde çalışırken tutarlı bir görünüm sağlamak için snooping veya directory-based protokoller gibi önbellek tutarlılık protokollerinin merkezinde yer alır.
Çalışma Prensipleri
Önbellek mekanizmasının temel işleyişi, bir talep geldiğinde verinin hiyerarşik olarak aranması ve yönetilmesi sürecine dayanır. İşlemci tarafından üretilen bir bellek adresi (genellikle sanal adres) öncelikle adres parçalama (address slicing) işlemine tabi tutulur. Bu adres genellikle Tag (etiket), Index (dizin) ve Block Offset (blok ofseti) olarak üç bileşene ayrılır. Index bölümü, önbellekte hangi satırın (veya kümenin) kontrol edileceğini belirlerken, Tag bölümü o satırda bulunan verinin ait olduğu ana bellek adres bölgesini tanımlar. Block Offset ise, bulunan bir önbellek satırı (bloğu) içindeki spesifik bayta erişmek için kullanılır.
Bir önbellek erişimi sırasındaki karar akışı kritik bir yol üzerindedir. İşlemci adresi gönderdiğinde, önbellek kontrolörü index değerini kullanarak ilgili satırı ve onun tag değerini okur. Bu tag değeri, gelen adresin tag bölümü ile karşılaştırılır. Eğer eşleşme varsa ve satır geçerli (valid bit = 1) ise, bir hit gerçekleşir ve ofset değeri ile belirlenen veri işlemciye iletilir. Eşleşme yoksa, bir miss durumu oluşur. Bu noktada sistem, veriyi bir alt seviye bellekten (L2, L3 veya ana bellek) getirme sürecini başlatır. Bu getirme işlemi sırasında işlemci duraklatılabilir (stall) veya out-of-order execution ile diğer talimatlar çalıştırılmaya devam edilebilir.
Miss durumunda, getirilecek veri için önbellekte boş bir satır bulunması gerekir. Eğer Index ile belirlenen satır (veya küme içindeki tüm satırlar) dolu ise, bir yer değiştirme algoritması (replacement policy) devreye girer. En yaygın algoritmalar arasında Least Recently Used (LRU), First-In, First-Out (FIFO) ve Random bulunur. LRU, en uzun süredir kullanılmayan bloğu değiştirmeyi hedefler ve yüksek bulma oranı sağlar, ancak uygulama maliyeti yüksektir. Rastgele seçim ise basit ve düşük maliyetlidir, ancak tahmin edilemez bir performans profili çizer.
Önbellek tasarımında kritik bir diğer kavram, prefetching (önceden getirme)'dir. Bu teknik, işlemcinin açık bir isteği olmadan, gelecekte ihtiyaç duyulacağı tahmin edilen veri bloklarının önbelleğe önceden yüklenmesini sağlar. Prefetching, bellek erişimlerindeki düzenlilikleri (örneğin, sıralı erişim) tespit eden donanımsal veya yazılımsal algoritmalarla çalışır. Başarılı bir prefetch, ıska oranını önemli ölçüde düşürerek performansı artırır; ancak yanlış tahminler gereksiz bellek bant genişliği tüketimine ve yararlı verilerin önbellekten atılmasına neden olabilir.
Önbellek organizasyonu, adres eşleme yöntemine göre performansı derinden etkiler. Doğrudan eşlemeli (direct-mapped) önbellekte, her ana bellek bloğu önbellekte yalnızca bir olası yere yazılabilir. Bu, düşük donanım karmaşıklığı ve hızlı erişim sunar, ancak sıkışma ıskalarına (conflict miss) yol açabilir. Tam ilişkili (fully associative) önbellekte ise bir blok önbellektki herhangi bir boş yere yerleştirilebilir, bu da çok düşük çakışma ıskası sağlar, ancak bir bloğu bulmak için tüm satırların tag karşılaştırması yapılması gerekir, bu da yüksek güç tüketimi ve karmaşıklık demektir. Küme ilişkili (set-associative) önbellek, bu iki uç arasında bir denge kurar; önbellek, her biri N satır içeren kümeler halinde düzenlenir (N-way set associative). Bir blok, belirli bir küme içindeki herhangi bir satıra yerleştirilebilir, bu da karmaşıklığı makul seviyede tutarken çakışma ıskalarını azaltır.
// Basitleştirilmiş bir önbellek hit/miss kontrolü mantığını gösteren psödokod
struct CacheLine {
int valid;
int tag;
int data[BLOCK_SIZE];
};
CacheLine cache[NUM_SETS][ASSOCIATIVITY];
int access_cache(int address) {
int index = (address >> OFFSET_BITS) & (NUM_SETS - 1);
int tag = address >> (OFFSET_BITS + INDEX_BITS);
// Küme içinde tag arama (N-way associative)
for (int i = 0; i < ASSOCIATIVITY; i++) {
if (cache[index][i].valid && cache[index][i].tag == tag) {
// HIT: Veriyi bul ve güncelle (LRU vb.)
update_replacement_info(index, i);
return cache[index][i].data[extract_offset(address)];
}
}
// MISS: Yer değiştirme ve veri getirme
int victim_way = select_victim(index); // LRU, Random vb.
cache[index][victim_way].valid = 1;
cache[index][victim_way].tag = tag;
fetch_data_from_memory(address, &cache[index][victim_way].data);
return cache[index][victim_way].data[extract_offset(address)];
}
- Adres Parçalama: Bellek adresinin Tag, Index ve Block Offset bileşenlerine ayrılması işlemi.
- Tag Karşılaştırması: Önbellekteki etiketin, istenen adresin etiketi ile eşleşip eşleşmediğinin kontrolü.
- Yer Değiştirme Algoritması (LRU, FIFO): Tüm satırlar dolu olduğunda hangi bloğun değiştirileceğine karar veren politika.
- Prefetching: Gelecekteki erişimleri tahmin ederek veriyi proaktif olarak önbelleğe getirme tekniği.
Yerellik İlkeleri
Önbellek sistemlerinin etkinliği, bilgisayar programlarının bellek erişim davranışlarında gözlemlenen iki temel yerellik (locality) ilkesine dayanır: zamansal yerellik (temporal locality) ve mekansal yerellik (spatial locality). Bu ilkeler, önbelleğin neden çalıştığının teorik temelini oluşturur ve performans optimizasyonlarının odak noktasıdır. Zamansal yerellik, yakın gelecekte tekrar erişileceği öngörüsüyle, kısa süre önce erişilmiş bir veri öğesine veya talimata yeniden erişme eğilimini ifade eder. Bu ilke, döngü sayaçları, sık kullanılan değişkenler veya fonksiyon çağrılarında sıkça karşılaşılan bir durumdur.
Mekansal yerellik ise, bir bellek konumuna erişildiğinde, yakın gelecekte ona bitişik veya yakın bellek konumlarına da erişilme olasılığının yüksek olduğu prensibidir. Bu durum, diziler (arrays) üzerinde sıralı tarama yapan programlar veya birbirini takip eden talimatların (program counter'ın artması) getirilmesi sırasında belirgindir. Önbellek tasarımcıları, bu iki ilkeden maksimum düzeyde faydalanmak için stratejiler geliştirirler. Örneğin, zamansal yerelliği desteklemek için LRU gibi akıllı yer değiştirme algoritmaları kullanılırken, mekansal yerelliği artırmak için ana bellekten tek seferde getirilen veri bloğunun boyutu (cache line size) optimize edilir. Daha büyük önbellek satırları, bir erişimde daha fazla bitişik veriyi getirerek mekansal yerellikten fayda sağlar, ancak daha fazla bant genişliği tüketir ve getirme süresini uzatabilir.
Yerellik ilkelerinin pratikteki etkisi, önbellek ıska türlerinin sınıflandırılmasında da görülür. Zorunlu ıska (compulsory miss) veya soğuk ıska, bir bloğa ilk kez erişildiğinde meydana gelir ve kaçınılmazdır. Kapasite ıskası (capacity miss), programın aktif çalışma kümesi (working set) önbellek kapasitesini aştığında oluşur. Çakışma ıskası (conflict miss) ise, doğrudan eşlemeli veya küme ilişkili önbelleklerde, farklı ana bellek bloklarının önbellekte aynı satıra veya küme indeksine eşlenmek zorunda kalmasından kaynaklanır. İyi bir önbellek tasarımı ve algoritma optimizasyonu, kapasite ve çakışma ıskalarını minimize etmeyi hedefler, ancak zorunlu ıskalar genellikle prefetching teknikleri ile azaltılabilir.
Yerellik, sadece donanım değil, yazılım tasarımını da yönlendiren bir kavramdır. Veri yapıları ve algoritmalar, mümkün olduğunca bu ilkelere saygı göstererek yazılmalıdır. Örneğin, büyük bir matris üzerinde satır bazlı (row-major) erişim yapmak, C/C++ gibi satır bazlı düzen kullanan dillerde güçlü bir mekansal yerellik sağlar. Buna karşın, sütun bazlı erişim, sık sık önbellek ıskalarına neden olabilir. Benzer şekilde, döngü birleştirme (loop fusion) gibi derleyici optimizasyonları, aynı veri üzerinde çalışan ve ayrı döngülerde bulunan işlemleri birleştirerek, verinin önbellekten atılmadan önce defalarca kullanılmasını sağlar, böylece zamansal yerelliği artırır.
Yazma Politikaları
İşlemcinin önbellekte bulunan bir veri üzerinde yazma (store) işlemi gerçekleştirmesi durumunda, bu güncellemenin ne zaman ve nasıl ana belleğe yansıtılacağını belirleyen kurallar bütününe yazma politikası (write policy) denir. Bu politika, sistemin performansı ile tutarlılık ve veri bütünlüğü arasında kritik bir denge kurar. Temelde iki ana yaklaşım vardır: Write-Through (Doğrudan Yazma) ve Write-Back (Geri Yazma). Write-Through politikasında, her yazma işlemi hem önbellek satırına hem de ana belleğe aynı anda yazılır. Bu, ana belleğin her zaman güncel veriyi tutmasını sağlar, ancak her yazma işleminin yüksek gecikmeli ana bellek erişimi gerektirmesi nedeniyle sistem bant genişliğini tüketir ve yazma performansını düşürür.
Write-Back politikası ise performans odaklıdır. Bu yaklaşımda, yazma işlemi yalnızca önbellek satırına yapılır ve ana bellek güncellenmez. Bu durumu işaretlemek için ilgili önbellek satırına bir dirty bit (kirli bit) eklenir ve bu bit 1 (true) olarak ayarlanır. Ana belleğe yazma işlemi, kirli olarak işaretlenmiş bir satırın, yer değiştirme nedeniyle önbellekten atılması gerektiği ana kadar ertelenir. Bu, tekrar eden yazma işlemlerini ana bellek gecikmesinden koruyarak önemli bir performans kazancı sağlar. Ancak, bu politika ana belleğin bir süre güncel olmayan veri tutmasına neden olur ve çok işlemcili (multiprocessor) sistemlerde tutarlılık protokollerinin daha karmaşık olmasını gerektirir.
Her iki politikayı da desteklemek ve yazma ıskalarının (write miss) yönetimi için ek stratejiler geliştirilmiştir. Bir yazma ıskası olduğunda, iki temel yaklaşım mevcuttur: Write Allocate (Yazma-Tahsis) ve No-Write Allocate (Yazma-Tahsis Yok). Write Allocate politikasında, yazılmak istenen blok önce önbelleğe getirilir (allocate edilir), ardından yazma işlemi yukarıda seçilen write-through veya write-back politikasına göre bu yeni satır üzerinde gerçekleştirilir. No-Write Allocate'te ise blok önbelleğe getirilmez, yazma işlemi doğrudan bir alt seviye belleğe (genellikle ana bellek veya bir alt seviye önbellek) yönlendirilir. Genel olarak, Write-Back politikası Write Allocate ile, Write-Through politikası ise No-Write Allocate ile birlikte kullanılma eğilimindedir.
Bu politikaların seçimi, iş yükünün doğasına ve sistem mimarisinin hedeflerine bağlıdır. Yüksek yazma yoğunluklu iş yükleri (örneğin, veritabanı işlem günlüğü), Write-Back politikasından büyük ölçüde fayda görür. Buna karşılık, paylaşımlı bellekli çok işlemcili sistemlerde veya bellek eşlenmiş G/Ç (memory-mapped I/O) cihazları ile çalışırken, verinin ana bellekte her an güncel olmasını gerektiren durumlarda Write-Through politikası gerekli olabilir. Modern işlemciler, performansı en üst düzeye çıkarmak için genellikle L1 veri önbelleğinde Write-Back politikasını kullanırken, yazma tamponları (write buffers) ve birleştiricileri (write combiners) gibi tekniklerle Write-Through'un getirdiği gecikmeyi azaltmaya çalışırlar.
Önbellek Tutarlılığı
Çok çekirdekli (multicore) ve çok işlemcili (multiprocessor) sistemlerde, aynı bellek konumunun birden fazla önbellekte kopyasının bulunabilmesi, önbellek tutarlılığı (cache coherence) sorununu doğurur. Tutarlılık, tüm işlemcilerin paylaşılan bir bellek konumu için tek ve tutarlı bir değer görmesini garanti eden bir durumdur. Temel sorun, bir işlemcinin kendi yerel önbelleğindeki bir veriyi güncellemesi durumunda, bu verinin diğer işlemcilerin önbelleklerindeki eski kopyalarının geçersiz hale gelmesidir. Bu sorunu çözmek için donanım tabanlı önbellek tutarlılık protokolleri geliştirilmiştir.
En yaygın tutarlılık protokolleri, snooping (gözetleme) protokolü ve directory-based (dizin tabanlı) protokol olarak ikiye ayrılır. Snooping protokolünde, tüm önbellek kontrolörleri, paylaşılan bir veri yolu (bus) veya interconnect üzerinden iletilen tüm işlemleri (okuma/yazma talepleri) izler (snoop). Eğer bir işlem, kendi önbelleklerinde tutulan bir adresi etkiliyorsa, bu kopya üzerinde gerekli eylemi (geçersiz kılma veya veriyi güncelleme) gerçekleştirirler. Bu protokol, merkezi bir yapıya ihtiyaç duymaması ve küçük ölçekli sistemlerde düşük gecikme sağlaması nedeniyle yaygındır. Ancak, veri yolu üzerindeki trafiği artırarak ölçeklenebilirliği sınırlar.
Dizin tabanlı protokol ise, ölçeklenebilirliği daha yüksek sistemler için tasarlanmıştır. Bu yaklaşımda, paylaşılan belleğin veya son seviye önbelleğin (LLC) belirli bir bölümü, hangi işlemci önbelleklerinin hangi bellek bloklarının kopyasını tuttuğunu gösteren merkezi bir dizin (directory) yapısı olarak işlev görür. Bir işlemci bir bloğu yazmak istediğinde, önce dizine başvurur; dizin, o bloğun kopyalarını tutan diğer tüm işlemcileri belirler ve onlara bu kopyaları geçersiz kılma (invalidate) mesajları gönderir. Bu yöntem noktadan noktaya (point-to-point) iletişim kullandığı için veri yolu çakışmasını azaltır ve büyük ölçekli çok işlemcili sistemlerde daha verimlidir, ancak dizin yapısının ek donanım maliyeti ve erişim gecikmesi vardır.
Tutarlılık protokolleri, önbellek satırlarının durumlarını yönetmek için genellikle MSI (Modified, Shared, Invalid) veya onun gelişmiş versiyonları olan MESI (Modified, Exclusive, Shared, Invalid) ve MOESI (Modified, Owned, Exclusive, Shared, Invalid) gibi durum makinelerini kullanır. Örneğin, MESI protokolünde: Modified (M) durumundaki bir satır sadece yerel önbellekte bulunur, değiştirilmiştir (dirty) ve ana bellekten farklıdır. Exclusive (E) durumu, satırın yalnızca bu önbellekte bulunduğunu ve ana bellek ile tutarlı olduğunu gösterir. Shared (S) durumu, satırın bir veya daha fazla önbellekte okuma için bulunabileceğini ve ana bellek ile tutarlı olduğunu ifade eder. Invalid (I) durumu ise satırın geçersiz (kullanılamaz) olduğu anlamına gelir. Bu durumlar arasındaki geçişler, yerel işlemci ve uzak işlemcilerden gelen snoop isteklerine yanıt olarak gerçekleşir.