İçeriğe geç
Educora
Üniversite25 dk53 / 59

Bilgisayar mimarisi

Von Neumann mimarisini, işlemcinin komut döngüsünü ve yazmaçlarını, önbellek hiyerarşisini, ikiye tümleyende ikilik aritmetiği ve IEEE 754 kayan noktalı sayıların temellerini öğren.

Kendini test et
Bu derste öğreneceklerin
  • Bir von Neumann bilgisayarının parçalarını ve getir–çöz–yürüt döngüsünü açıklamak
  • İşlemci süresini ve ortalama bellek erişim süresini hesaplamak
  • Tam sayıları ikiye tümleyende yazmak ve taşmayı tanımak
  • Bir sayıyı IEEE 754 biçiminde kodlamak ve yuvarlama hatalarını açıklamak

Telefonundaki 3 GHz'lik işlemci saniyede 3 milyar tik atar, ama Python'da 0.1 + 0.2 == 0.3 yazarsan cevap False olur. İki gerçek de bilgisayarın nasıl kurulduğundan kaynaklanır: işlemci komutları tek tek getirip yürütür ve sayıları sonlu sayıda bitte saklar. Bu derste donanımın programcıyı en çok etkileyen parçalarını inceleyeceğiz.

Von Neumann mimarisi ve komut döngüsü

Tanım
Von Neumann mimarisi

Program ve veriler aynı bellekte tutulur; işlemci (aritmetik mantık birimi, denetim birimi ve yazmaçlar) komutları bellekten sırayla getirip yürütür; hepsi bir yolla (bus) bağlıdır. Programı değiştirmek için kabloları değil, belleğin içeriğini değiştirmek yeter.

YazmaçGörevi
PC — program sayacısonraki komutun adresi
IR — komut yazmacışu an yürütülen komut
MAR / MDRbellek adresi ve oradan okunan/yazılan veri
ACC ve genel yazmaçlarhesaplamaların ara sonuçları
Bayraklar (flags)sıfır, işaret, elde ve taşma bitleri
  1. 1
    Getir (fetch)

    PC'deki adres MAR'a geçer, komut bellekten okunup IR'ye yazılır, PC bir sonraki komuta ilerler.

  2. 2
    Çöz (decode)

    Denetim birimi işlem kodunu ve işlenenleri belirler.

  3. 3
    Yürüt (execute)

    ALU hesaplar ya da veri belleğe yazılır/okunur; atlama komutu PC'yi değiştirir. Sonra döngü yinelenir.

Python
memory = [('LOAD', 5), ('ADD', 6), ('STORE', 7), ('PRINT', 7), ('HALT', 0), 20, 22, 0]
pc, acc, executed = 0, 0, 0
while True:
    op, addr = memory[pc]
    pc += 1
    executed += 1
    if op == 'LOAD':
        acc = memory[addr]
    elif op == 'ADD':
        acc += memory[addr]
    elif op == 'STORE':
        memory[addr] = acc
    elif op == 'PRINT':
        print('memory[7] =', memory[addr])
    elif op == 'HALT':
        break
print('instructions executed:', executed, '| PC =', pc, '| ACC =', acc)
▸ Beklenen çıktı
memory[7] = 42
instructions executed: 5 | PC = 5 | ACC = 42
Akümülatörlü oyuncak bir işlemci: 0–4 hücrelerinde komutlar, 5–7 hücrelerinde veriler var; von Neumann ilkesine göre aynı dizide. Döngü: getir (memory[pc]), PC'yi artır, çöz (if op == …), yürüt.
t = N · CPI / ft = N · CPI / f
burada:
  • tprogramın işlemci süresi, s
  • Nyürütülen komut sayısı
  • CPIkomut başına ortalama saat döngüsü
  • fsaat frekansı, Hz

İşlemci başarımının “temel denklemi”: hızlanmak için ya daha az komut (daha iyi algoritma ve derleyici), ya daha küçük CPI (boru hattı, önbellek) ya da daha yüksek frekans gerekir.

Örnek 1: işlemci süresi

Bir program 2 · 10⁹ komut yürütüyor, CPI = 1,5, frekans 3 GHz. İşlemci süresi nedir? Derleyici komut sayısını %20 azaltır ama CPI 1,6'ya çıkarsa ne olur?

Çözümü göster
t = 2 · 10⁹ · 1,5 / (3 · 10⁹) = 3 · 10⁹ / 3 · 10⁹ = 1 s.
Yeni sürüm: N = 1,6 · 10⁹, t = 1,6 · 10⁹ · 1,6 / (3 · 10⁹) = 2,56 / 3 ≈ 0,853 s.
Komut başına döngü artsa da toplam süre ≈ %15 azaldı; tek bir ölçüte bakarak karar verilmez.

Bellek hiyerarşisi ve önbellek

İşlemci ana bellekten (RAM) yaklaşık yüz kat hızlıdır; buna von Neumann darboğazı denir. Çözüm bellek hiyerarşisidir: işlemcinin yanında küçük ve hızlı önbellek düzeyleri, uzakta büyük ve yavaş bellekler. Önbellek yerellik ilkesine dayanır: yakın zamanda kullanılan veri yakında yine kullanılır (zamansal yerellik), komşu adresler de öyle (uzamsal yerellik). Bu yüzden bellek 64 baytlık önbellek satırlarıyla getirilir.

DüzeyTipik boyutYaklaşık gecikme
Yazmaçlarbirkaç yüz bayt≈ 1 döngü
L1 önbellekçekirdek başına 32–64 KB≈ 1 ns
L2 önbellekçekirdek başına 0,25–2 MB≈ 3–5 ns
L3 önbellekortak, onlarca MB≈ 10–20 ns
Ana bellek (RAM)gigabaytlar≈ 60–100 ns
SSDyüzlerce GB – TB≈ 20–100 µs
Modern bir masaüstü işlemci için büyüklük mertebeleri (kesin değerler modele bağlıdır). Her aşağı basamak yaklaşık 3–10 kat yavaş ama daha büyüktür.
AMAT = thit + m · tmiss
burada:
  • AMATortalama bellek erişim süresi
  • thitönbellekte isabet olduğunda erişim süresi
  • mıskalama oranı
  • tmissıskalama cezası (bir sonraki düzeye gitmek)
Örnek 2: iki düzeyli önbellek

L1: 1 ns, ıskalama oranı %5. L2: 4 ns, kendisine ulaşan isteklerin %20'sini ıskalıyor. RAM: 100 ns. AMAT'ı bul. L2 olmasaydı ne olurdu?

Çözümü göster
L1'in ıskalama cezası L2'nin kendi AMAT'ıdır: 4 + 0,2 · 100 = 24 ns.
AMAT = 1 + 0,05 · 24 = 1 + 1,2 = 2,2 ns.
L2 olmadan: AMAT = 1 + 0,05 · 100 = 6 ns, yani yaklaşık 2,7 kat yavaş.
Sonuç: önbellek dostu kod (diziyi sırayla gezmek), dağınık erişimlerden (bağlı liste) çok daha hızlıdır.

Tam sayılar: ikiye tümleyen

n bitlik ikiye tümleyende (two's complement) en yüksek bitin ağırlığı negatiftir: −2ⁿ⁻¹. Bu yüzden negatif sayılar 1 ile başlar ve ayrı bir “çıkarıcıya” gerek kalmaz: ALU, a − b'yi a + (−b) olarak hesaplar. Toplamanın kendisi mantık kapılarından kurulur: yarım toplayıcıda toplam biti a XOR b, elde ise a AND b'dir.

−x = (NOT x) + 1; n bit: −2ⁿ⁻¹ … 2ⁿ⁻¹ − 1
burada:
  • NOT xx'in tüm bitlerinin tersine çevrilmesi
  • nbit sayısı (8 bit: −128 … 127)
Örnek 3: −45 ve taşma

a) −45'i 8 bitlik ikiye tümleyende yaz. b) 8 bitlik işaretli aritmetikte 100 + 50 kaç olur?

Çözümü göster
a) 45 = 00101101 → ters çevir: 11010010 → +1: 11010011.
Sağlama: −128 + 64 + 16 + 2 + 1 = −45 ✓ (işaretsiz okunursa 211 = 256 − 45).
b) 100 = 01100100, 50 = 00110010, toplam = 10010110.
En yüksek bit 1 oldu → −128 + 16 + 4 + 2 = −106. İki pozitif sayının toplamı negatif çıktı; bu taşmadır (overflow): 150 > 127. Sonuç başa sarar: 150 − 256 = −106.
Etkileşimli
Simülasyon yükleniyor…
11010011'i gir: işaretsiz 211'dir, ikiye tümleyende 211 − 256 = −45. Sonra 10000000 (−128) ve 01111111 (127) değerlerini, yani 8 bitin sınırlarını dene.
Etkileşimli
Simülasyon yükleniyor…
Yarım toplayıcının toplam biti: XOR yalnızca girişler farklıyken 1 verir (1 + 1 = 10₂ — toplam 0, elde 1).

Kayan noktalı sayılar: IEEE 754

x = (−1)ˢ · 1,m₂ · 2^(e − 127)
burada:
  • sişaret biti (1 bit)
  • ekaydırılmış üs (8 bit, sapma 127)
  • mmantisin kesir kısmı (23 bit; baştaki 1 yazılmaz)

32 bitlik (single) biçim. 64 bitlik double: 1 + 11 + 52 bit, sapma 1023, yaklaşık 15–16 ondalık basamak duyarlık. Python float'u bir double'dır.

Örnek 4: −6,25'i kodlamak

−6,25'i IEEE 754 single biçiminde yaz (bitler ve on altılık kod).

Çözümü göster
İşaret: negatif → s = 1.
6,25 = 6 + 0,25 = 110₂ + 0,01₂ = 110,01₂ = 1,1001₂ · 2².
Üs: e = 2 + 127 = 129 = 10000001₂.
Mantis (baştaki 1 olmadan): 1001 ve ardından 19 sıfır.
Bitler: 1 | 10000001 | 10010000000000000000000
4'lü gruplar: 1100 0000 1100 1000 0000 … → C0C80000₁₆.
Python
import struct, math

def to_bits(x, bits=8):
    return format(x % 2 ** bits, '0' + str(bits) + 'b')

def from_bits(s):
    v = int(s, 2)
    return v - 2 ** len(s) if s[0] == '1' else v

print(to_bits(45), to_bits(-45), from_bits('11010011'))
print('100 + 50 in 8 bits:', from_bits(to_bits(100 + 50)))

raw = struct.pack('>f', -6.25).hex()
print('-6.25 as float32:', raw, format(int(raw, 16), '032b'))
print(0.1 + 0.2, 0.1 + 0.2 == 0.3, math.isclose(0.1 + 0.2, 0.3))
print(struct.unpack('>f', struct.pack('>f', 0.1))[0])
▸ Beklenen çıktı
00101101 11010011 -45
100 + 50 in 8 bits: -106
-6.25 as float32: c0c80000 11000000110010000000000000000000
0.30000000000000004 False True
0.10000000149011612
x % 2 ** bits sayıyı n bitlik “çembere” sarar; ikiye tümleyenin kendisi budur. struct gerçek 32 biti gösterir: elle bulduğumuz C0C80000 ile aynı. 0,1 ikilik sistemde sonsuz bir kesirdir (0,000110011…₂); bu yüzden float32'de 0,10000000149…, double'da ise 0,1 + 0,2 = 0,30000000000000004 olur.

Önemli noktalar

  • Von Neumann bilgisayarında program ve veri aynı bellektedir; işlemci getir–çöz–yürüt döngüsünü yineler.
  • İşlemci süresi t = N · CPI / f'dir; başarım üç etkene de bağlıdır.
  • Önbellek yerelliğe dayanır; AMAT = thit + m · tmiss.
  • İkiye tümleyen: −x = NOT x + 1; n bit −2ⁿ⁻¹ … 2ⁿ⁻¹ − 1 aralığını kapsar; aralığın dışına çıkmak taşmadır.
  • IEEE 754: işaret, kaydırılmış üs, mantis; 0,1 gibi sayılar tam yazılamaz, bu yüzden float'ları == ile karşılaştırma.

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
−20 sayısının 8 bitlik ikiye tümleyeni nedir?