Bilgisayar bir işi nasıl yapar? İşlemci, bellek ve önbellek
İşlemci saniyede milyarlarca adım atar ama çoğu zaman veriyi beklemekle geçirir. Getir-çöz-yürüt döngüsünü, saat sinyalini, yazmaçları ve önbellekten diske uzanan bellek katmanlarını, sayıları kendin girebildiğin bir atölye çizimiyle anlatıyoruz.
Yazan: Kuazara Bilim EkibiYayın: Güncelleme: 8 dk okuma
Bu yazıda · 10 bölüm
Bir hesap makinesine 7 + 3 + 5 + 2 yazdığında sonuç göz açıp kapayıncaya kadar ekrana gelir. Telefonundaki işlemci bu toplamayı saniyenin milyarda biri kadar bir sürede yapabilir. Peki o kadar hızlıysa, bilgisayarlar neden bazen “düşünür”? Bir programın açılması neden saniyeler sürer? Cevabın büyük kısmı hesaplamada değil, veriyi beklemekte saklı. Bu yazıda bilgisayarın içini bir atölye gibi açıyoruz: işi yapan bir işçi, önünde birkaç küçük çekmece, yanında küçük ama hızlı bir raf, arkada büyük bir dolap ve en uzakta bir arşiv deposu.
İşlemci ne yapar? Getir, çöz, yürüt
Bilgisayardaki her program, işlemcinin anladığı çok basit komutlardan oluşan uzun bir listedir: “şu sayıyı bellekten getir”, “iki sayıyı topla”, “sonucu belleğe yaz”, “şu koşul doğruysa listenin şu satırına atla”. İşlemci bu listeyi tek tek, hep aynı üç adımlık döngüyle işler:
- Getir (fetch): Sıradaki komut bellekten alınır. Hangi komutun sırada olduğunu program sayacı adlı küçük bir sayaç tutar.
- Çöz (decode): Komutun ne istediği anlaşılır: toplama mı, bellekten okuma mı, atlama mı? Hangi yazmaçlar kullanılacak?
- Yürüt (execute): İş yapılır. Toplama için aritmetik-mantık birimi çalışır; bellekten okuma için veri istenir ve gelmesi beklenir.
Sonra program sayacı bir sonraki komutu gösterir ve döngü baştan başlar. Bu fikir 1940’larda, ilk elektronik bilgisayarlar tasarlanırken netleşti: komutlar da veri gibi bellekte durur ve işlemci onları sırayla okur. Bugün cebimizdeki telefon da, dev sunucular da aynı temel düzenle çalışır.
Saat sinyali: işlemcinin metronomu
İşlemcinin içindeki milyarlarca transistör adımlarını bir saat sinyaliyle uyumlu atar. Saat, saniyede belirli sayıda “tık” üreten bir elektrik sinyalidir; her tık bir çevrim (cycle) sayılır. “3 GHz” yazan bir işlemcinin saati saniyede 3 milyar kez tıklar; yani bir çevrim yaklaşık üçte bir nanosaniye sürer. Bu sürede ışık bile ancak 10 santimetre kadar yol alır.
Basit bir işlemcide bir komutun her evresi bir ya da birkaç çevrim sürer. Gerçek işlemciler daha akıllıdır: bir komutu yürütürken sonrakini çözer, ondan sonrakini getirir. Fabrikadaki üretim bandına benzeyen bu düzene boru hattı (pipeline) denir; böylece ortalamada her çevrimde bir, hatta birkaç komut tamamlanabilir. Ama hepsinin ortak bir sınırı vardır: veri gelmeden onunla iş yapılamaz.
Yazmaçlar: işçinin önündeki çekmeceler
İşlemcinin hemen içinde yazmaç (register) denen çok az sayıda, çok hızlı bellek gözü bulunur. Toplama makinesi sayıları doğrudan yazmaçlardan alır ve sonucu yine bir yazmaca yazar. Bugünkü yaygın işlemcilerde programın doğrudan kullanabildiği genel amaçlı yazmaç sayısı onlarla ölçülür ve her biri 64 bitlik bir sayı tutar. Yani bütün yazmaçlar toplam birkaç yüz bayt eder. Yazmaca erişmek beklemesizdir: veri, işlemcinin bir çevrimde kullanabileceği yerdedir.
Sorun şu: programların verisi gigabaytlarca tutar. Bir fotoğraf bile milyonlarca sayıdır. Yazmaçlara sığmayan her şey daha uzakta, bellek dediğimiz yerdedir ve uzaklık zaman demektir.
Bellek neden katmanlıdır?
İdeal bellek hem çok büyük, hem çok hızlı, hem ucuz, hem de elektrik kesildiğinde bilgiyi unutmayan bir bellek olurdu. Böyle bir bellek yok. Hızlı bellek türleri (işlemcinin içindeki SRAM hücreleri) bit başına pahalıdır ve çok yer kaplar; büyük ve ucuz olanlar (RAM’deki DRAM, SSD’deki flaş bellek) yavaştır. Mühendislerin bulduğu çözüm, birkaç türü katman katman dizmektir: işlemciye en yakın katman küçük ve hızlı, uzaklaştıkça büyüyen ama yavaşlayan katmanlar.
Aşağıdaki tablo, bugünkü masaüstü ve dizüstü bilgisayarlar için yaklaşık büyüklük sıralarını gösterir. Kesin değerler işlemciden işlemciye değişir; burada önemli olan, katmanlar arasındaki farkın yüzde değil kat cinsinden olmasıdır. Süreleri 3 GHz’lik bir işlemcinin çevrimlerine de çevirdik.
| Katman | Tipik boyut | Erişim süresi (yaklaşık) | 3 GHz’de çevrim |
|---|---|---|---|
| Yazmaçlar | birkaç yüz bayt | beklemesiz | 0–1 |
| L1 önbellek | onlarca KB (çekirdek başına) | ~1 ns | ~4 |
| L2 önbellek | yüzlerce KB – birkaç MB | ~3–5 ns | ~10–15 |
| L3 önbellek | birkaç MB – onlarca MB | ~10–20 ns | ~30–60 |
| RAM (ana bellek) | 8–64 GB | ~60–100 ns | ~200–300 |
| SSD | yüzlerce GB – birkaç TB | ~10–100 µs | on binlerce – yüz binlerce |
| Sabit disk (HDD) | birkaç TB | ~5–10 ms | on milyonlarca |
Bu sayıları insan ölçeğine taşımak yardımcı olur. Önbellekten bir sayı almak bir saniye sürseydi, RAM’den almak neredeyse bir dakika sürerdi, SSD’den okumak birkaç saate hatta bir güne, dönen bir sabit diskten okumak ise aylara uzanırdı. İşlemci bu sürelerde boş durmamak için elinden geleni yapar, ama her zaman başaramaz.
Atölyede bir program
Aşağıdaki çizimde bu katmanları bir atölye olarak kurduk. Sağdaki tezgâh işlemci: delikli bir şerit programı taşır, okuyucu sıradaki komutu okur (getir), lambalar komutun türünü gösterir (çöz), toplama makinesi ve işçi kol işi yapar (yürüt). Metronom saat sinyalidir. Kolun önünde R1 ve R2 adlı iki yazmaç yuvası var. Kolun hemen yanındaki gözlü raf önbellek, arkadaki çekmeceli dolap RAM, en uzaktaki raf ise disktir. Önbellekte olmayan veriyi raydaki vagon getirir.
Program bir sayı listesini üç tur toplar. Her tur R1’i sıfırlar, sayıları R2’ye tek tek getirip R1’e ekler; bir oyunun her karede aynı veriyi yeniden işlemesi gibi. Önce varsayılan değerlerle Adım düğmesiyle ilerle: her komutun getir, çöz ve yürüt evrelerini ayrı ayrı göreceksin. Sonra şunları dene:
- İlk turda her sayı için kolun önbellekte eli boş kaldığını, ikinci turdan itibaren ise sayıları raftan hemen aldığını izle. Saat tıkı sayacına bak: ilk tur neredeyse bütün zamanı yer.
- Önbelleği 2 göze indir ve programı bitir. Kaç isabet çıktı? Nedenini aşağıda anlatıyoruz.
- Erişim düzenini Dağınık yap: sayılar belleğe dağılınca diskten kaç sayfa okunduğuna bak.
- Kendi sayılarını yaz (ör. 3, 7, 12) ya da gecikmeleri değiştir. Örneğin disk gecikmesini bir sabit diskinkine, on milyonlarca çevrime çıkar.
Çizimdeki vagonun yol süresi gerçek ölçekte değildir; gerçek ölçek kullanılsaydı disk yolculuğu, önbellekten okumadan on binlerce kat uzun sürerdi. Sayaçlar ise çevrimleri modelden doğru sayar.
Önbellek isabeti ve ıskası
İşlemci bir veriye ihtiyaç duyduğunda önce önbelleğe bakar. Veri oradaysa buna isabet (hit) denir ve birkaç çevrimde gelir. Yoksa ıska (miss) olur: veri bir sonraki katmandan, en kötü durumda RAM’den ya da diskten getirilir ve bir kopyası önbelleğe konur. Önbellek doluysa yer açmak için içindekilerden biri çıkarılır. En yaygın kurallardan biri, en uzun süredir kullanılmayanı çıkarmaktır (İngilizce kısaltmasıyla LRU).
Ortalama erişim süresini kabaca şöyle hesaplayabiliriz:
ortalama süre = isabet süresi + ıska oranı × ıska bedeli
Çizimdeki varsayılan sayılarla, isabet süresi 4 çevrim ve ıska bedeli (RAM’e gitmek) 200 çevrimdir. Okumaların %90’ı isabetse ortalama 4 + 0,1 × 200 = 24 çevrimdir. İsabet oranı %50’ye düşerse ortalama 4 + 0,5 × 200 = 104 çevrime çıkar; yani dört kattan fazla yavaşlar. Gerçek işlemcilerde iyi çalışan programların önbellek isabet oranları çoğunlukla çok yüksektir; küçük bir oran değişikliğinin bu kadar büyük fark yaratması bu yüzden önemlidir.
Çizimde önbelleği 2 göze indirdiğinde ilginç bir şey olur: 7, 3, 5, 2 listesini sırayla okuyan program hiç isabet alamaz. Çünkü iki gözlü önbellek her yeni sayıda en eski sayıyı çıkarır ve çıkan sayı, tam iki adım sonra yeniden gereken sayıdır. Çalışılan veri önbelleğe sığmayınca önbellek “biraz az” değil, hiç işe yaramayabilir. Programcılar bu yüzden veriyi önbelleğe sığacak parçalara bölerek işlemeye çalışır.
Yerellik: küçük bir raf neden bu kadar işe yarar?
Önbellek, RAM’den yüzlerce hatta binlerce kat küçüktür. Buna rağmen okumaların büyük çoğunluğunu karşılayabilmesinin nedeni, programların davranışındaki bir düzenliliktir. Buna yerellik denir ve iki türü vardır:
- Zamansal yerellik: Az önce kullanılan veri, yakında yine kullanılır. Bir döngünün sayacı, bir oyundaki oyuncunun konumu, sık çağrılan bir fonksiyon böyledir.
- Uzamsal yerellik: Bir veri kullanıldıysa, bellekte hemen yanındaki veri de yakında kullanılır. Bir listeyi baştan sona okumak buna örnektir.
Gerçek önbellekler uzamsal yerellikten yararlanmak için veriyi tek tek değil, önbellek satırı denen bloklar hâlinde (bugünkü işlemcilerin çoğunda 64 bayt) getirir. Çizimdeki model daha basittir: her göz bir sayı tutar. Ama RAM ile disk arasında aynı fikri görebilirsin: disk veriyi 4 sayılık sayfalar hâlinde RAM’e taşır. Sayılar yan yana durduğunda tek bir sayfa okuması hepsini getirir; dağınık durduğunda her sayfa için diske ayrı ayrı gitmek gerekir.
RAM ve disk: uçucu ve kalıcı
Yazmaçlar, önbellek ve RAM uçucu bellektir: elektrik kesilince içerikleri kaybolur. Disk (SSD ya da sabit disk) ise kalıcıdır. Programlar ve dosyalar bu yüzden diskte saklanır ve çalıştırılacakları zaman RAM’e yüklenir. Bir uygulamanın ilk açılışının yavaş, ikinci açılışının daha hızlı olmasının bir nedeni budur: işletim sistemi az önce okuduğu dosyaları bir süre RAM’de tutar.
Programın ihtiyaç duyduğu bir sayfa RAM’de yoksa buna sayfa hatası (page fault) denir; işletim sistemi sayfayı diskten okur. Bu sırada işlemci o program için bekler ama genellikle başka bir programa geçerek boş durmaz. RAM yetmeyince bilgisayarın gözle görülür biçimde yavaşlamasının nedeni, sayfaların sürekli diske gidip gelmesidir.
Gerçek işlemciler daha da akıllı
Çizimdeki işlemci kasıtlı olarak basit: komutları tek tek işler ve veri beklerken durur. Gerçek işlemciler beklemeyi gizlemek için birçok yöntem kullanır:
- Çok katmanlı önbellek: Çoğu işlemcide L1, L2 ve L3 olmak üzere üç düzey vardır; L1 genellikle komutlar ve veriler için ikiye ayrılır.
- Önceden getirme (prefetching): İşlemci, bir listeyi sırayla okuduğunu fark ederse sonraki verileri istenmeden önce getirmeye başlar.
- Sıra dışı yürütme: Bir komut veri beklerken, ona bağlı olmayan sonraki komutlar öne alınıp yürütülür.
- Çok çekirdek: Birden çok işlemci çekirdeği aynı anda farklı işleri yürütür; her çekirdeğin kendi L1 ve çoğunlukla L2 önbelleği olur.
Bu yöntemlerin hepsi aynı gerçeğe karşı geliştirilmiştir: işlemciler son on yıllarda bellekten çok daha hızlı gelişti. Mühendisler bu farka “bellek duvarı” adını verir.
Özet
- İşlemci programı küçük komutlar hâlinde, getir, çöz, yürüt döngüsüyle işler; adımlarını saat sinyali belirler.
- Yazmaçlar işlemcinin içindeki birkaç hızlı gözdür; geri kalan veri katman katman uzaklaşan belleklerdedir.
- Katmanlar arasında hız farkı kat cinsindendir: önbellek birkaç çevrim, RAM yüzlerce, disk on binlerce ve daha fazla çevrim sürer.
- Önbellekte bulunan veri isabet, bulunmayan ıskadır; ortalama erişim süresi ıska oranına çok duyarlıdır.
- Önbellek, programların yerellik eğilimi sayesinde küçük olmasına rağmen işe yarar. Çalışılan veri sığmazsa etkisi tamamen kaybolabilir.
Sık sorulanlar
4 soruİşlemci saniyede kaç işlem yapar?
Önbellek ile RAM arasındaki fark nedir?
Önbellek ıskası ne demektir?
Bilgisayar kapanınca RAM'deki bilgiler neden silinir?
Kaynaklar
4 kaynak- 1.Patterson, D. A. ve Hennessy, J. L., Computer Organization and Design: The Hardware/Software Interface, Morgan Kaufmann (bellek hiyerarşisi bölümü)
- 2.Hennessy, J. L. ve Patterson, D. A., Computer Architecture: A Quantitative Approach, Morgan Kaufmann (ortalama bellek erişim süresi, önbellek tasarımı)
- 3.Bryant, R. E. ve O'Hallaron, D. R., Computer Systems: A Programmer's Perspective, Pearson (bellek hiyerarşisi ve yerellik)
- 4.Norvig, P., Teach Yourself Programming in Ten Years (norvig.com; tipik işlem sürelerinin yaklaşık tablosu) (yeni sekmede açılır)norvig.com
- #bilgisayar
- #işlemci
- #bellek
- #önbellek
- #ram
- #saat sinyali
- #mühendislik