---
title: 'Katman Önbelleği'
source: 'https://academia.sh/tr/kurslar/konteynerler/katman-onbellegi'
course: Konteynerler
language: tr
updated: '2026-08-17T18:09:04+00:00'
license: 'CC BY-SA 4.0'
---

# Katman Önbelleği

Yönerge sırasının derleme işine etkisi ölçülür: katman anahtarı önceki katmanın anahtarıyla yönergenin girdi özetinden türetilir, iki yönerge sırası aynı sekiz derlemelik değişiklik dizisinde koşturulur ve önbellek isabet oranı, yeniden üretilen katman sayısı ile yazılan bayt karşılaştırılır.

Önceki ders her yönergeden bir katman doğduğunu ölçtü ve tek bir `calistir` satırının imajın
büyük bölümünü ürettiğini saydı. O ölçüm tek bir derlemeyi kapsıyordu. Bir servis günde birkaç
kez derlenirse aynı satır her seferinde aynı işi yapar — oysa girdisi değişmediyse çıktısı da
değişmez ve katmanlar zaten içerik özetiyle adlanır.

Bu ders o tekrarı kaldıran yapıyı kurar. **Katman önbelleği**, üretilmiş bir katmanı anahtarıyla
saklar; sonraki derlemede aynı anahtar çıkarsa katman yeniden üretilmez, saklanandan alınır.
Anahtarın neyden türediği bir karar doğurur: yönergelerin **sırası**. Ölçülecek şey de odur —
bağımlılık kurulumunun mu yoksa kaynak kopyalamanın mı önce yazıldığı, aynı değişiklik dizisinde
isabet oranını, yeniden üretilen katman sayısını ve yazılan baytı ne kadar oynatır?

**IM13.** Önbellek `node` ile kurulan bir modeldir; anahtarlar bir eşlemede tutulur, katmanlar
gerçekten `onbellek/<anahtar>/` altına yazılır. Ağaç önceki dersin ağacının bu ölçüm için
sadeleştirilmiş biçimidir; bayt değerleri o dersin sayılarıyla karşılaştırılmaz. **IM14.** Katman
anahtarı, önceki katmanın anahtarı ile yönergenin **girdi özetinden** türer. `kopyala` için girdi
özetine kopyalanacak dosyaların içeriği girer; `calistir` için yalnız satırın kendisi girer.
**IM15.** Süre ölçülmez. Ölçülen nicelikler koşumdan bağımsızdır: yeniden üretilen katman sayısı
ve yazılan bayt.

## Katman Anahtarı

Önbelleğin çalışması tek kurala bağlıdır: **bir katmanın anahtarı, altındaki her şeyi ve kendi
girdisini kapsamalıdır.** Kapsamazsa değişmiş bir alt katmanın üstündeki katman yanlışlıkla
isabet eder ve imaj tutarsız çıkar. Kural, önceki dersin zincir kimliğinin aynısıdır; farkı,
zincire yönergenin girdisinin de katılmasıdır.

```js
// olcum-agi/onbellek.mjs — katman onbellekli yorumlayici (model)
import { mkdirSync, writeFileSync, readFileSync, readdirSync, appendFileSync, statSync, rmSync } from "node:fs";
import { createHash } from "node:crypto";

const KOK = "proje";
const ozet = (s) => createHash("sha256").update(s).digest("hex").slice(0, 12);
export const metin = (ad, n) => Array.from({ length: n }, (_, i) => `// ${ad} ${i}\n`).join("");

const AGAC = {
  "bagimlilik-listesi": "csv 2.4\nkuyruk 1.9\n",
  "kaynak/giris.js": metin("giris", 20),
  "kaynak/topla.js": metin("topla", 140),
  "kaynak/kuyruk.js": metin("kuyruk", 95),
  "kaynak/sinama/topla.sinama.js": metin("sinama", 210),
};
export const URETIM = {
  "kosum-tabani": { "kok/kosum.js": metin("kosum", 420), "kok/kabuk.js": metin("kabuk", 60) },
  "bagimlilik-kur": { "bagimlilik/csv.js": metin("csv", 900), "bagimlilik/kuyruk.js": metin("kuyruk-bag", 1400) },
};

export const SIRA_A = `temel     kosum-tabani
kopyala   bagimlilik-listesi  /uygulama/liste
calistir  bagimlilik-kur
kopyala   kaynak              /uygulama/kaynak`;

export const SIRA_B = `temel     kosum-tabani
kopyala   kaynak              /uygulama/kaynak
kopyala   bagimlilik-listesi  /uygulama/liste
calistir  bagimlilik-kur`;

// Degisiklik dizisi: her derlemeden once dokunulan dosya, bos ise degisiklik yok.
export const DEGISIKLIK = ["", "kaynak/topla.js", "kaynak/giris.js", "", "bagimlilik-listesi",
  "kaynak/topla.js", "kaynak/sinama/topla.sinama.js", "kaynak/kuyruk.js"];

const yazDosya = (yol, icerik) => {
  mkdirSync(yol.split("/").slice(0, -1).join("/"), { recursive: true });
  writeFileSync(yol, icerik);
};
export const kurAgac = () => {
  rmSync(KOK, { recursive: true, force: true });
  rmSync("onbellek", { recursive: true, force: true });
  for (const [y, i] of Object.entries(AGAC)) yazDosya(`${KOK}/${y}`, i);
};
export const degistir = (yol, no) => appendFileSync(`${KOK}/${yol}`, `// duzeltme ${no}\n`);

const gez = (alt = "") => readdirSync(`${KOK}/${alt}`, { withFileTypes: true }).flatMap((g) =>
  g.isDirectory() ? gez(`${alt}/${g.name}`) : [`${alt}/${g.name}`.replace(/^\//, "")]).sort();
const yollari = (kaynak) => statSync(`${KOK}/${kaynak}`).isDirectory() ? gez(kaynak) : [kaynak];

// Katman anahtari: onceki katmanin anahtari + yonergenin girdi ozeti.
// kopyala icin girdi ozetine kopyalanacak dosyalarin icerigi de girer; calistir icin yalniz satir.
const girdiOzeti = (satir) => {
  const [yonerge, kaynak] = satir.trim().split(/\s+/);
  if (yonerge !== "kopyala") return ozet(satir);
  return ozet(satir + yollari(kaynak).map((y) => y + readFileSync(`${KOK}/${y}`)).join());
};

export const derle = (tanim, onbellek) => {
  let anahtar = "";
  const sonuc = { isabet: 0, uretim: 0, bayt: 0, zincir: [] };
  for (const satir of tanim.trim().split("\n")) {
    anahtar = ozet(anahtar + girdiOzeti(satir));
    sonuc.zincir.push(anahtar);
    if (onbellek.has(anahtar)) { sonuc.isabet++; continue; }
    const [yonerge, kaynak, hedef] = satir.trim().split(/\s+/);
    const ciftler = yonerge === "kopyala"
      ? yollari(kaynak).map((y) => [`${hedef.slice(1)}/${y}`, readFileSync(`${KOK}/${y}`)])
      : Object.entries(URETIM[kaynak]);
    let bayt = 0;
    for (const [y, icerik] of ciftler) { yazDosya(`onbellek/${anahtar}/${y}`, icerik); bayt += icerik.length; }
    onbellek.set(anahtar, bayt);
    sonuc.uretim++; sonuc.bayt += bayt;
  }
  return sonuc;
};
```

Anahtarın zincirlenmesinin doğrudan sonucu şudur: bir yönerge ıskaladığında üstündeki **her**
yönerge de ıskalar, girdisi hiç değişmemiş olsa bile. Önbellek yukarıdan aşağı değil, aşağıdan
yukarı çalışır ve ilk ıskalama noktasından sonrası tümüyle yeniden üretilir.

## İki Sıra, Aynı Değişiklik Dizisi

İki tanım dosyası aynı dört yönergeyi taşır, yalnız sıraları farklıdır. Sıra A bağımlılık
listesini kopyalayıp kurulumu yapar, kaynağı en sona bırakır; sıra B kaynağı en başa alır. Her
iki tanım da aynı sekiz derlemelik dizide koşturulur; dizinin her adımı bir dosyaya tek satır
ekler, iki koşumda da birebir aynı sırayla.

**IM16.** Değişiklik dizisi kurgudaki okuma toplayıcı servisinin bir haftalık düzeltme akışını
temsil eder: soğuk önbellekle ilk derleme, beş kaynak değişikliği, bir bağımlılık listesi
değişikliği ve hiçbir şeyin değişmediği bir derleme. **IM17.** Her sıra kendi boş önbelleğiyle başlar; önbellek koşumlar arasında
paylaşılmaz.

```js
// olcum-agi/kosum.mjs — iki yonerge sirasini ayni degisiklik dizisinde kosturur
import { kurAgac, degistir, derle, DEGISIKLIK, SIRA_A, SIRA_B } from "./onbellek.mjs";

const kosumYap = (ad, tanim) => {
  kurAgac();
  const onbellek = new Map();
  const toplam = { isabet: 0, uretim: 0, bayt: 0 };
  console.log(`\n${ad}`);
  console.log(" no  degisiklik".padEnd(38) + "isabet  uretim     bayt");
  DEGISIKLIK.forEach((d, i) => {
    if (d) degistir(d, i);
    const s = derle(tanim, onbellek);
    for (const k of Object.keys(toplam)) toplam[k] += s[k];
    console.log(`  ${i + 1}  ${d || (i ? "(degisiklik yok)" : "(ilk derleme)")}`.padEnd(38) +
      String(s.isabet).padStart(4) + String(s.uretim).padStart(8) + String(s.bayt).padStart(9));
  });
  const deneme = DEGISIKLIK.length * tanim.trim().split("\n").length;
  console.log(`  toplam: ${toplam.isabet}/${deneme} isabet (%${((toplam.isabet * 100) / deneme).toFixed(1)}), ` +
    `uretilen katman ${toplam.uretim}, yazilan ${toplam.bayt} bayt, onbellekte ${onbellek.size} girdi ` +
    `${[...onbellek.values()].reduce((a, b) => a + b, 0)} bayt`);
  return toplam;
};

const a = kosumYap("A — bagimlilik once, kaynak sonra", SIRA_A);
const b = kosumYap("B — kaynak once, bagimlilik sonra", SIRA_B);
console.log(`\nB'nin A'ya gore fazladan urettigi katman: ${b.uretim - a.uretim}`);
console.log(`B'nin A'ya gore fazladan yazdigi bayt   : ${b.bayt - a.bayt} (%${(((b.bayt - a.bayt) * 100) / a.bayt).toFixed(1)} daha fazla is)`);
```

```
A — bagimlilik once, kaynak sonra
 no  degisiklik                       isabet  uretim     bayt
  1  (ilk derleme)                       0       4    47354
  2  kaynak/topla.js                     3       1     6009
  3  kaynak/giris.js                     3       1     6023
  4  (degisiklik yok)                    4       0        0
  5  bagimlilik-listesi                  1       3    41336
  6  kaynak/topla.js                     3       1     6037
  7  kaynak/sinama/topla.sinama.js       3       1     6051
  8  kaynak/kuyruk.js                    3       1     6065
  toplam: 20/32 isabet (%62.5), uretilen katman 12, yazilan 118875 bayt, onbellekte 12 girdi 118875 bayt

B — kaynak once, bagimlilik sonra
 no  degisiklik                       isabet  uretim     bayt
  1  (ilk derleme)                       0       4    47354
  2  kaynak/topla.js                     1       3    41308
  3  kaynak/giris.js                     1       3    41322
  4  (degisiklik yok)                    4       0        0
  5  bagimlilik-listesi                  2       2    35313
  6  kaynak/topla.js                     1       3    41350
  7  kaynak/sinama/topla.sinama.js       1       3    41364
  8  kaynak/kuyruk.js                    1       3    41378
  toplam: 11/32 isabet (%34.4), uretilen katman 21, yazilan 289389 bayt, onbellekte 21 girdi 289389 bayt

B'nin A'ya gore fazladan urettigi katman: 9
B'nin A'ya gore fazladan yazdigi bayt   : 170514 (%143.4 daha fazla is)
```

## Sıranın Sayısı

Aynı dört yönerge, aynı sekiz değişiklik, iki farklı sonuç. Sıra A **32 katman denemesinin
20'sinde** isabet eder — yüzde 62,5 — ve **12 katman** üretir. Sıra B aynı dizide **11 isabet**,
yüzde 34,4 ve **21 katman** verir. Yazılan bayt 118 875'e karşı 289 389'dur: sıra B aynı işi
yapmak için **yüzde 143,4 daha fazla** bayt yazar.

Farkın tamamı beş satırda toplanır — kaynak değişikliği olan derlemelerde. Sıra A'da kaynak
kopyalama en üsttedir, altındaki hiçbir katman etkilenmez ve yalnız **6 000 bayt** civarında bir
katman yeniden üretilir. Sıra B'de kaynak kopyalama en alttadır; kaynağa dokunulduğu anda
üstündeki liste kopyalama ve bağımlılık kurulumu da geçersizleşir ve **41 300 bayt** yazılır.
Yedi kat fark, kaynak dosyalarının küçüklüğünden değil, **üstlerinde ne olduğundan** gelir.

Beşinci derleme ters yönü gösterir. Bağımlılık listesi değiştiğinde sıra A üç katman üretir
(41 336 bayt), sıra B iki katman üretir (35 313 bayt) — bu derlemede B daha ucuzdur. Karar tek bir
derlemeye bakarak verilemez; dizinin bileşimine bakarak verilir. Sekiz derlemenin beşinde kaynak,
birinde bağımlılık listesi değişmiştir ve gerçek bir servis ağacında oran bundan daha da keskindir.
Kural şu biçimde yazılır: **yönergeler değişme sıklığına göre sıralanır, en seyrek değişen en
alta.** Bu, önceki dersin katman sırası kuralının derleme tarafındaki karşılığıdır ve iki kural
aynı ölçüden çıkar.

Dördüncü derleme de sayılır: hiçbir şey değişmediğinde iki sıra da 4 isabet ve 0 bayt verir.
Önbelleğin kazancı yalnız sıraya değil, değişikliğin varlığına da bağlıdır — değişmeyen bir ağaçta
her iki sıra da bedava çalışır.

İsabet oranının kendisi de dikkatle okunur. Payda, katman denemesi sayısıdır: dört yönergelik bir
tanımda sekiz derleme 32 deneme eder. Yönerge sayısı artarsa payda büyür ve aynı davranış daha
yüksek bir oran gösterebilir; oran tek başına iki farklı tanım dosyasını karşılaştırmaya yetmez.
Karar taşıyan iki sayı **yeniden üretilen katman** ile **yazılan bayttır**, çünkü ikisi de yapılan
işin doğrudan ölçüsüdür ve tanımın satır sayısından bağımsızdır. Bu ölçümde üçü de aynı yönü
gösterdiği için sıra kararı tartışmasızdır; göstermedikleri durumda bakılacak sütun bayt
sütunudur.

Önbelleğin kendi bedeli son sütundadır. Sıra A'nın önbelleğinde **12 girdi ve 118 875 bayt**, sıra
B'ninkinde **21 girdi ve 289 389 bayt** birikir. Hiçbir girdi silinmez: her ıskalama diske yeni bir
katman yazar, eskisi yerinde kalır. Önbellek, derleme süresini bayt karşılığında satın alan bir
depodur ve kötü sıra bu depoyu da iki buçuk katına çıkarır.

Deponun nerede durduğu ayrıca sayılır. Katman önbelleği, derlemenin koştuğu makinede birikir; 12
ve 21 girdilik depolar o makinenin durumudur. Aynı tanım dosyası boş önbellekli bir makinede
koşturulduğunda her iki sıra da tablonun ilk satırındaki bedeli öder: 4 katman, 47 354 bayt. Sıra
kararının kazancı ancak ikinci derlemeden itibaren görünür. Önceki kursta ortam denkliği adıyla
ölçülen fark burada yeni bir biçim alır: derlemenin **sonucu** makineden bağımsızdır, **maliyeti**
değildir. Yalıtım bütçesinin bu derste ödenen kalemi budur — tekrarlanan iş ortamdan çıkarılmadı,
bir depoya taşındı ve o depo hâlâ ortamda duruyor.

## Önbelleğin Ölçmediği

Anahtar `calistir` yönergesi için yalnız satırın kendisini kapsar. Satır değişmedikçe katman
yeniden üretilmez — kurulacak şeyin dışarıda değişmiş olması anahtara girmez. **IM18.** Dış
dünyanın değişmesi, kurulum tablosundaki içeriğin büyütülmesiyle modellenir; liste dosyasına
dokunulmaz.

```js
// olcum-agi/bayat.mjs — onbellek anahtarinin olcmedigi girdi
import { kurAgac, degistir, derle, metin, URETIM, SIRA_A } from "./onbellek.mjs";

kurAgac();
const onbellek = new Map();
const ilk = derle(SIRA_A, onbellek);
const kurulumAnahtari = ilk.zincir[2];
console.log(`ilk derleme: ${ilk.uretim} katman, ${ilk.bayt} bayt`);

// Dis dunya degisti: kurulacak bagimliligin icerigi buyudu, liste dosyasi ayni kaldi.
URETIM["bagimlilik-kur"]["bagimlilik/csv.js"] = metin("csv", 950);
const guncel = Object.values(URETIM["bagimlilik-kur"]).reduce((t, i) => t + i.length, 0);
const s = derle(SIRA_A, onbellek);
console.log(`\nliste dosyasi ayni, kurulacak icerik degisti:`);
console.log(`  isabet ${s.isabet}, yeniden uretilen katman ${s.uretim}, yazilan ${s.bayt} bayt`);
console.log(`  onbellekteki kurulum katmani ${onbellek.get(kurulumAnahtari)} bayt, guncel icerik ${guncel} bayt`);
console.log(`  fark ${guncel - onbellek.get(kurulumAnahtari)} bayt, ayni anahtar: ${s.zincir[2] === kurulumAnahtari}`);

degistir("bagimlilik-listesi", 9);
const t = derle(SIRA_A, onbellek);
console.log(`\nliste dosyasina tek satir eklenince:`);
console.log(`  isabet ${t.isabet}, yeniden uretilen katman ${t.uretim}, yazilan ${t.bayt} bayt`);
console.log(`  kurulum katmani ${onbellek.get(t.zincir[2])} bayt`);
```

```
ilk derleme: 4 katman, 47354 bayt

liste dosyasi ayni, kurulacak icerik degisti:
  isabet 4, yeniden uretilen katman 0, yazilan 0 bayt
  onbellekteki kurulum katmani 35280 bayt, guncel icerik 35830 bayt
  fark 550 bayt, ayni anahtar: true

liste dosyasina tek satir eklenince:
  isabet 1, yeniden uretilen katman 3, yazilan 41858 bayt
  kurulum katmani 35830 bayt
```

Yalıtımın delindiği yer burasıdır. Kurulacak içerik **550 bayt** büyüdüğü hâlde dört yönergenin
dördü de isabet eder, sıfır katman üretilir ve imaj eski içeriği taşımayı sürdürür. Anahtar
aynıdır, çünkü anahtar `calistir` satırının **metnini** ölçer, satırın **sonucunu** değil. Önbellek
doğru çalışmaktadır; ölçtüğü şey eksiktir.

Bu, önceki kursta ölçülen yeniden üretilebilirlik sorununun önbellek tarafındaki biçimidir. Bir
`calistir` satırı çıktısı zamana bağlı bir iş yapıyorsa — sürüm sınırı yazılmamış bir kurulum
gibi — aynı satır iki farklı zamanda iki farklı katman üretir, ama önbellek ikisini de aynı
anahtarla adlandırır. Çözüm önbellekte değil, yönergededir: `calistir`ın girdisi bir dosyaya
yazılırsa, o dosya `kopyala` ile bağlama girer ve içeriği anahtara katılır. Üçüncü blokta liste
dosyasına tek satır eklendiğinde tam olarak bu olur — anahtar değişir, kurulum katmanı yeniden
üretilir ve depodaki katman 35 830 bayta güncellenir.

Kopyalama tanesi de bir eksiklik kalemidir. Yedinci derlemede değişen dosya bir sınama
dosyasıdır; koşumda hiç çalışmayacak bir dosya, `kaynak` dizini tek yönergeyle kopyalandığı için
6 051 baytlık katmanı yeniden ürettirir. Önceki dersin ölçtüğü gereksiz dosyalar burada ikinci kez
bedel ödetir: önce imaj boyutunda, sonra önbellek isabetinde.

## Özet

- Katman anahtarı, önceki katmanın anahtarı ile yönergenin girdi özetinden türer. Bir yönerge
  ıskaladığında üstündeki her yönerge de ıskalar; önbellek ilk ıskalama noktasından sonrasını
  tümüyle yeniden üretir.
- Aynı dört yönerge, aynı sekiz derleme: bağımlılık kurulumu önce yazıldığında 32 denemenin
  20'si isabet etti (yüzde 62,5), 12 katman üretildi, 118 875 bayt yazıldı. Kaynak kopyalama
  öne alındığında isabet 11'e düştü (yüzde 34,4), katman 21'e, yazılan bayt 289 389'a çıktı —
  yüzde 143,4 daha fazla iş.
- Fark kaynak değişikliği olan derlemelerde toplanır: sıra A'da 6 000 bayt civarı, sıra B'de
  41 300 bayt civarı. Bağımlılık listesi değiştiğinde yön tersine döner; kararı tek derleme değil,
  değişiklik dizisinin bileşimi verir.
- Önbelleğin kendi bedeli bir depodur ve yalnız büyür: sıra A 12 girdi ve 118 875 bayt, sıra B
  21 girdi ve 289 389 bayt biriktirdi.
- Anahtar `calistir` için yalnız satırın metnini ölçer. Kurulacak içerik 550 bayt büyüdüğü hâlde
  dört yönerge de isabet etti ve sıfır katman üretildi; imaj eski içeriği taşımayı sürdürdü.
  Girdi bir dosyaya yazılıp bağlama alındığında anahtar değişir ve katman güncellenir.

## Sonraki Adım

Önbellek tekrarlanan işi kaldırdı ama imajın içeriğine hiç dokunmadı. Sekiz derlemenin
sonunda ortaya çıkan imaj, ilk derlemenin ürettiği yığının aynısıdır: bağımlılık kurulumunun
bıraktığı her şey, kopyalanan her kaynak dosyası ve kurulum sırasında kullanılan her araç son
katmanda durur. Oysa bunların bir bölümü yalnız **derleme sırasında** gerekliydi; koşum anında
çalışan tek şey uygulamanın kendisidir. Sonraki ders bu ayrımı ölçer: aynı uygulama tek aşamada
ve çok aşamada derlendiğinde son imajın boyutu, katman sayısı ve içinde kalan gereksiz dosya
sayısı ne kadar ayrılır, ve çok aşamalı derlemenin önbellek tarafındaki bedeli nedir?
