Basit Bir İşlemci Nasıl Çalışır?

Barış Ekin Yıldırım
8 min readSep 30, 2019

Click for English Translation

Mikroişlemci ya da işlemci olarakta bilinen Central Processing Unit(CPU) adından da anlaşılacağı üzere verileri işlemeye yarar. İşleyeceği veriler programa göre değişir. Bu bir oyunda olabilir üzerinde çalıştığınız bir LibreOffice dosyasıda. Program her ne olursa olsun CPU bunu önemsemez, çünkü işlediği verileri son kullanıcının gördüğü gibi görmez. Yalnızca programın hangi instructionlara sahip olduğuna bakar.

Bir programı çalıştırmak için ikonuna tıkladığınız olan şeyler şunlardır;

  • Depolama biriminde barınan program RAM’e yüklenir. Program instructionlardan oluşur.
  • CPU, memory controller adı verilen bir devreyi kullanarak programı Ram’den çeker ve kendi üzerine yükler.
  • Veri şu an CPU’nun içindedir ve işlenmeye başlar.
  • Bu andan sonra ne olacağı programa bağlıdır. İşlemci verilen komuta bakarak ya işlemeye devam eder ya da işlediği veriyle bir şey yapar, mesela ekrana basmak gibi.

Geçmişte CPU, depolama birimi ve RAM arasındaki veri aktarımını kendi başına kontrol ediyordu. Depolama birimleri RAM’ yavaş olduğu için CPU aktarımın gerçekleşmesini uzunca bir süre beklemek zorunda kalıyor ve sistem yavaşlıyordu. Processor I/O denen bu işlem Direct Access Memory(DMA) denen bir sistemin gelişiyle ortadan kalktı. Artık veri aktarımları DMA üzerinden yapılacak bu da işlemciyi gereksiz yere meşgul etmeyecekti.

Saat ve Çarpanlar

Saat, bilgisayarın içindeki işlemleri senkronize etmek için kullanılan bir sinyaldir. Bir saat sinyali sabit şekilde sürekli kare dalga üretir, ürettiği dalgalar sürekli 0 ve 1 olarak döner durur. Aşağıdaki görselde her birine “tick” denen üç tam cycle(döngü) görürsünüz. Saat sinyali Hertz(Hz) ile ölçülür, Hertz her saniye üretilen cycle sayısına işaret eder. 100 Mhz’lik bir saat saniyede 100 milyon cycle üretiyor demektir.

Şekil 1.0 - Saat sinyali

Bilgisayarlarda her şey cyclelar ile ölçülür. Örneğin “10” birimlik gecikmeye sahip bir hafıza, 10 tam cycle sonrası veri aktarımına başlayacak demektir. CPU içindeki her bir instruction belli gecikme sürelerine sahiptir. Mesela, x komutu 6 tam cycle sonrası çalışmaya başlayacaktır. CPU’nun ilginç yanlarından biride verilen komutların her birinin ne kadar sürelik gecikmeye sahip olduğunu bilmesidir. Bunu yapabilmesi için üzerinde gecikme tabloları tutar.

Peki saatin performansla ne gibi bir alakası var? Saat hızı ve performans ilişkisi genellikle insanların kafası karıştırır. Eğer tam olarak aynı iki CPU’yu ele alıyorsak saat hızı yüksek olanın performansı da yüksek demektir. Çünkü saat hızı yüksek olan daha hızlı işlem yapabilecektir. Ancak iki farklı işlemciyi ele alıyorsak olay bu şekilde ilerlemek zorunda değildir. Örneğin ARM ve Intel arasında bu şekilde bir karşılaştırma yaparsak bu çok saçma olacaktır, çünkü ikisininde içindeki dizayn farklıdır. Saat hızları eşitken Intel’de 5 döngü alan bir komut ARM’de 7 döngü alıyorsa, doğal olarak Intel daha hızlı işlem yapacaktır.

Konu modern işlemciler olunca işin içine, farklı önbellek boyutları, farklı sayıda execution unitler, farklı instruction tipleri ve çalışma zamanları vb. girmektedir.

Eğer işlemci saat sinyali çok fazla artarsa bu çok büyük bir soruna yol açar. Anakartın üzerindeki saat sinyali artık işlemcininkiyle eşzamanlı gidemez. Daha önce anakart incelediyseniz üzerindeki veri hatlarını görmüşsünüzdür. İşte saat sinyalinin hızı çok yükselirse o veri hatları anten gibi davranmaya başlayacak, bu da verinin çok alakasız bir şekilde radyo sinyaline dönüşmesiyle sonuçlanacaktır.

Şekil 1.1 — Anakart üzerindeki bir veri hatları

Buna çözüm olarak işlemci üreticileri saat çarpanı adını verdikleri yeni bir konsept kullanmaya başladılar. Bu konseptte, işlemcinin içine dahili bir saat koyulur ve bu saat anakart üstündeki saati çarparak kullanır. Örneğin bir Intel Core i7 işlemci, 19 saat çarpanına sahiptir. Anakart üzerindeki 200 MHz’lik hızı 19'la çarparsak işlemcinin saat hızı 3.8GHz olacaktır.(anakartı hızı tamamen farazidir)

Bu durumda bile dahili saat ve harici saat arasındaki fark hala büyük bir performans kaybına yol açmaya devam eder. Zira işlemci içerisinde 3.8 GHz olan saat hızı RAM’den veri okumaya başlayınca tekrar 200 MHz’e düşecektir çünkü kuzey köprüsüne uğraması gerekir. Bu hız farkı işlemci içine eklenen önbelleklerle bir nebze olsun kapatılabiliyor. Başka bir yöntem ise bir cycleda aktarılan veri sayısını çoğaltmak. Döngü başına iki parça veri aktarımı DDR(Dual Data Rate), dört parça veri aktarımı ise QDR(Quad Data Rate) olarak adlandırılır.

Şekil 1.2 — Tek cycleda birden fazla veri aktarmak

Basitçe Modern Bir İşlemcinin Mimarisi

Aşağıdaki görselde modern bir işlemcinin epeyce basitleştirilmiş mimarisi verilmiştir. Gördüğünüz şema genel amaçlı bir modern işlemci birim olarak tasarlanmıştır, AMD, Intel ve ARM daha spesifik şemalar kullanırlar.

Şekil 1.3 — Modern CPU şeması

Şemadaki çizgili kısım CPU’nun içine temsil ediyor. RAM ve CPU arasındaki veri yolu genellikle 64 ila 128 bit(dual channel memory aktifse) arasındadır. Hangisi o an daha düşükse RAM ya da CPU saat hızına göre çalışır.

x clocks per sec. × y lines per clock × 64 bits per line × z interfaces = xxGB/s

şeklinde bir formülle aktarımın teorik üst limit oranı bulunabilir.

Çizgili kısımlar içerisindeki bütün birimler CPU dahili saatine göre çalışır. CPU’suna göre bazı birimler dahili saatten daha hızlı bile çalışabilir. Ayrıca birimler arasındaki veri yolu da çok daha hızlı olabilir. Örneğin modern işlemcilerde genellikle L2 memory cache ve L1 data cache arasındaki genişlik 256 bittir. Bant genişliği ne kadar yüksekse transferde o kadar yüksektir. Şemadaki okların renkleri farklı saat hızlarını gösterir.

Memory Cache

Memory cache, static memoryde denen yüksek performanslı bir bellek çeşitidir. Bilgisayarlarda RAM olarak kullanan bellekler ise dynamic memorydir. Static memory, daha fazla güç tüketimine sahiptir, daha fazla yer kaplar ve daha pahalıdır. CPU ile aynı saat hızında çalışabilir, ki bunu dynamic memory yapamaz.

Veri çekmek için sınırları dışına çıkmak, CPU’nun daha düşük bir saat hızında çalışmasını gerektireceği için kullanılırlar. CPU, belli bir pozisyondan bir datayı çektiğinde Memory Cache Controller denen bir devre araya girer ve memory cache’e tüm veri bloğunu yükler. Böylelikle, genelde programlar sıralı bir akışla çalıştığından, CPU’nun bir sonraki isteyeceği bellek pozisyonu memory cachede hazır bekliyor olur. Bundan dolayı memory cache ne kadar büyükse o kadar iyidir. CPU’nun önbelleğinden ulaşabildiği her veriye “hit” ulaşamadıklarına ise “miss” denir.

Örneğin; eğer CPU, adresi 1000 olan pozisyondan veri çekiyorsa, cache controller 1000 + n’inci adrese kadar çekip önbelleğe alacak demektir. Burada “n” olarak belirtilen sayıya “page” denir. Yani elinizdeki CPU 4KB’lık pagelerle çalışıyorsa, cache controller belirtilen adresten sonraki 4096 adresi de önbelleğe alacaktır.

Şekil 1.4 — Memory cache controller nasıl çalışır?

L1 ve L2, “Level 1” ve “Level 2” manalarına gelir, bu CPU çekirdeğinden ne kadar uzak olduklarını gösteren bir belirteçtir. Eğer Şekil 1.3'e göz attıysanız, aklınıza, iki tane L1 olmasına rağmen neden L2'den bir tane olduğu sorusu takılmış olabilir. Bunun sebebi, L1 instruction cache’in input cache olarak çalışırken, L1 data cache’in output cache olarak çalışmasıdır. L1 instruction cache genellikle L2 cache’den daha küçüktür, çünkü CPU’ya daha yakın olmasından ötürü loop tarzı işlemler burada tutulur. Bundan kaynaklı olarak L2'nin çok büyük alana ihtiyacı yoktur.

Branching

CPU’nun en önemli problemlerinden birisi çok fazla “miss” yaşamasıdır. Miss yaşanmaması içinse CPU kendi dışına çıkıp daha yavaş saat hızında çalışan RAM’e ulaşmalıdır. Genelde memory cache bunu önlemek için yeterli olsada bazı durumlarda çaresiz kalabilmektedir. Bir programın çalıştığını hayal edin, page sorunsuz bir şekilde yavaşça satır satır çalıştırılıyor, o da ne bir JMP instructiona denk geldiniz! Bu demek oluyor ki, n bir lokasyona zıpladınız. Bu yeni pozisyon L2 önbelleğine yüklenmeyecek, bilakis direk RAM üzerinde çalıştırılacak. Modern CPU’lar bu soruna çözüm olarak bir page’de JMP ifadesine denk gelirlerse, çalışmasını beklemeden gidip bir sonraki page’i getirirler.

Instructionları İşlemek

Fetch ünitesi bellekten instructionları getirir. İlk olarak CPU için gerekli olan instruction’ın L1 instruction cache’de olup olmadığını kontrol eder. Eğer yoksa L2 memory cache’e gider. Hala aradığını bulamamışsa çok daha yavaş olan RAM’e gider.

Fetch ünitesi, bellekten bir instruction çektikten sonra bunu Decode ünitesine gönderir. Decode ünitesi buna karar verirken CPU’nun içinde barınan bir ROM olan microcode’a başvurur. Microcode instruction’ın manasını çözdükten sonra, instruction’ı uygun olan Execute ünitesine gönderir. Microcode olmadan CPU instruction’ların ne manaya geldiğini anlayamaz. Microcode içerisinde bir instruction’ın nasıl çalıştığını gösteren adım adım tarifler vardır. CPU üzerinde barının bir ROM’u güncellemek mümkün olmadığı için, işletim sistemi üzerinden her bootta güncel microcode’u yükleyebilirsiniz. Ayrıca modern CPU’larda birden fazla instruction paralel olarak çalışabilir. Yine modern CPU’larda, her bir Execution ünitesi özelleştirilmiştir. Her ne kadar burada genel amaçlı bir işlemciden bahsediyor olsakta biraz detaya inmek faydalı olacaktır.

Bu özelleştirilmiş Execution ünitesine en iyi örnek FPU’dur. Floating Point Unit, kompleks matematiksel işlemlerin yapılması için devre bazında şekillendirilmiştir. Örneğin; eğer CPU üzerine gelen işlemin bir matematik işlemi olduğuna karar verdiyse(buna karar veren üniteye ise dispatch ya da schedule unit denir) bu işlemi FPU’ya gönderir, ALU’ya göndermez. İşlem bittikten sonra ise sonuçlar L1 data cache’e gönderilir.

CPU’lar üzerine enteresan olan başka bir şey ise, “pipelining” denen aynı anda birden fazla instruction’ı farklı stagelerde çalıştırabiliyor olmaları. Örneğin; Fetch ünitesi, Decode ünitesine bir instruction gönderdikten sonra boşa düşecektir. Bu durumda boşta durmamak için Fetch gidip sıradaki instruction’ı çekecektir. Fetch > Decode > Execution döngüsü böylelikle başlayacaktır. Yeni nesil Intel işlemciler 14 stage’e kadar çıkabilmektedirler. Bu birden fazla işlemin farklı stagelerde çalıştırılabildiği mimarilere ise “superscalar architecture” denmektedir.

Out-Of-Order Execution (OOO)

CPU’ların paralel işlem yeteneklerinden hemen yukarıda bahsetmiştik. ALU, FPU vb. birden fazla Execution ünitesi olduğunuda söylemiştik. Şimdi kabataslık bir örnek verelim ve elimizdeki CPU’nun 4 ALU, 2 tane de FPU ünitesine sahip olduğunu hayal edelim. Ve elimizde aşağıdaki gibi bir akış olsun.

1. genel instruction2. genel instruction3. genel instruction4. genel instruction5. genel instruction6. genel instruction7. matematiksel instruction8. genel instruction9. genel instruction10. matematiksel instruction

Burada olacak olan şey; schedule/dispatch ünitesi ilk 4 instruction’ı 4 ALU’ya gönderecek ve 5. instruction için ALU’lardan birinin boşalmasını bekleyecek. CPU bu sırada boş durmayacak, OOO ünitesi çalışmaya başlayacak ve 5 ve 6. instruction’ları atlayacak ve geri kalanlara bakacak. Sıra 7'ye geldiğinde matematiksel bir instruction olduğu için bunu FPU’ya gönderecek. Sonra tekrar sıradan bakmaya başlayacak ta ki, 10. instruction’ı da boşta kalan son FPU ünitesine gönderene dek. Böylelikle hiçbir ünite boş kalmayacak.

Speculative Execution

Peki yukarıdaki instruction’lar arasında bir loop olsaydı ne yapacaktık? CPU iki olasılığıda çalıştıracaktı. Şöyle ki;

1. genel instruction
2. genel instruction
3. eğer a=<b git 15. instruction
4. genel instruction
5. genel instruction
6. genel instruction
7. matematiksel instruction
8. genel instruction
9. genel instruction
10. matematiksel instruction
...
15. matematiksel instruction
16. genel instruction

Out-of-order ünitesi bu programı analiz ettikten sonra 15. instructionı alıp boştaki bir FPU’ya atacaktır, çünkü 3. instruction’ın bir sonucu olarak 15.’i görünmektedir. Eğer 3. instruction’ın sonucu a > b ise 15. instruction’ın işlemini iptal edecektir. a > b ise program normal seyrinde, eğer tersiyse program daha hızlı çalışacaktır, çünkü sonucu zaten en başından işlenip önbelleğe yüklenmiş olacaktır.

Makalede belirtilen işlemci spesifik bir modele ya da mimariye ait değildir, genel amaçlı işlemci(Generic Cpu) olarak ele alınmıştır. Yazarken olabildiğince Türkçe terimler kullanmaya çalıştım. Ne yazık ki çoğu kavramın karşılığı yok ve bunlara Türkçe karşılık uydurmak sadece okumayı zorlaştırıyor. Eğer bu konuda bir örnek görmek isterseniz; M. Morris Mano’nun Bilgisayar Sistemleri Mimarisi kitabının bir çevirisini satın alabilirsiniz. 1 bölümü dahi anlayabiliyorsanız kendinizi şanslı sayın.

Kaynakça:

--

--