---
title: 'Çok Aşamalı Derleme'
source: 'https://academia.sh/tr/kurslar/konteynerler/cok-asamali-derleme'
course: Konteynerler
language: tr
updated: '2026-08-17T18:09:04+00:00'
license: 'CC BY-SA 4.0'
---

# Çok Aşamalı Derleme

Derleme araçlarının çıktıdan ayrılması ölçülür: aynı uygulama tek aşamalı ve çok aşamalı olarak derlenir, son imajın boyutu, katman sayısı ve içinde kalan gereksiz dosya sayısı karşılaştırılır, çok aşamalının bedeli de sayılır — üretilen toplam bayt, gönderilmeyen ara katmanlar ve önbellek davranışı.

Önceki ders tekrarlanan işi kaldıran önbelleği kurdu ve yönerge sırasının bedelini saydı. O ölçüm
boyunca imajın **içeriğine** hiç dokunulmadı: sekiz derlemenin sonunda ortaya çıkan yığın, ilk
derlemenin ürettiğinin aynısıydı. Bağımlılık kurulumunun bıraktığı her dosya, kopyalanan her
kaynak dosyası ve kurulum sırasında kullanılan her araç son katmanda duruyordu.

Oysa bunların bir bölümü yalnız **derleme sırasında** gereklidir. Bir derleyici, bir bağlayıcı,
sınama koşucusu ve geliştirme bağımlılıkları çıktıyı üretmek için kullanılır; çıktı üretildikten
sonra hiçbiri çalıştırılmaz. **Çok aşamalı derleme** bu ayrımı yönerge düzeyinde kurar: derleme
kendi aşamasında yapılır, son imaja yalnız üretilen dosyalar taşınır. Ölçülecek soru bellidir —
son imajın boyutu, katman sayısı ve içinde kalan gereksiz dosya sayısı ne kadar ayrılır, ve bu
ayrımın bedeli nedir?

**IM19.** Aşağıdaki yorumlayıcı önceki derslerin modelinin genişletilmiş biçimidir: `asama`
yönergesi eklenir ve `kopyala` kaynağı `<asama>:<yol>` biçiminde yazıldığında önceki bir aşamanın
görünümünden okur. **IM20.** Derleme tabanı, koşum tabanının üstüne üç derleme aracı ekler; iki
taban arasındaki fark modelde açıkça yazılıdır. **IM21.** Paketleme modeli sınama dışı kaynak
dosyalarını ve koşum bağımlılıklarını tek dosyada birleştirir; geliştirme bağımlılıkları pakete
girmez. **IM22.** Koşumda gereken dosyalar elle tanımlanır: koşum tabanı dosyaları ve paketlenmiş
çıktı. Kurgudaki ölçüm ağının okuma toplayıcı servisi ölçülür.

## Derleme Zamanı ile Koşum Zamanı

Tek aşamalı tanımda bütün yönergeler tek bir yığına yazılır ve o yığın hem derlemeyi yapar hem de
çalıştırılır. Çok aşamalı tanımda `asama` yönergesi yığını böler: her aşama kendi tabanından
başlar, kendi zincirini kurar ve son aşama önceki aşamalardan yalnız istediği yolları alır. Son
imaj, **son aşamanın** katmanlarıdır; önceki aşamaların katmanları imaja hiç girmez.

Ayrımın zincir üzerindeki karşılığı da vardır. Her `asama` yönergesi anahtar zincirini sıfırlar:
ikinci aşamanın taban katmanı, birinci aşamada ne olduğundan bağımsız bir anahtar alır ve kaynak
değiştiğinde bile önbellekten gelir. İki aşama arasında geçen tek bağ, taşınan dosyaların
içeriğidir. Önceki dersin kuralı burada da geçerlidir — bir katmanın anahtarı altındaki her şeyi
kapsar — ama "altındaki her şey" artık aşama sınırında kesilir. Bu, çok aşamalı derlemenin
önbellek davranışını hem daha iyi hem de daha kötü yapan tek yapısal ayrımdır: ikinci aşamanın
tabanı bedavaya gelir, ama taşınan paket her değiştiğinde kopyalama katmanı yeniden üretilir.

```js
// olcum-agi/asama.mjs — asamali derleme yorumlayicisi ve katman onbellegi (model)
import { mkdirSync, writeFileSync, readFileSync, readdirSync, appendFileSync, rmSync } from "node:fs";
import { createHash } from "node:crypto";

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

const AGAC = { "kaynak/giris.js": metin("giris", 22), "kaynak/topla.js": metin("topla", 150),
  "kaynak/kuyruk.js": metin("kuyruk", 96), "kaynak/sinama/topla.sinama.js": metin("sinama", 205) };

const KOSUM_TABANI = { "kok/kosum.js": metin("kosum", 400), "kok/kabuk.js": metin("kabuk", 55) };
// Derleme tabani, kosum tabaninin ustune derleme araclarini ekler.
const DERLEME_TABANI = { ...KOSUM_TABANI, "arac/derleyici.js": metin("derleyici", 3200),
  "arac/baglayici.js": metin("baglayici", 1450), "arac/basliklar.js": metin("basliklar", 900) };

// Paketleme: sinama disi kaynak ile kosum bagimliliklari tek dosyada birlesir.
const paketle = (gorunum) => [...gorunum].filter(([y]) =>
  (y.startsWith("is/kaynak/") && !y.includes("/sinama/")) || y.startsWith("bagimlilik/kosum/"))
  .sort().map(([, i]) => i).join("");

const URETIM = {
  "derleme-tabani": () => DERLEME_TABANI,
  "kosum-tabani": () => KOSUM_TABANI,
  "bagimlilik-kur": () => ({ "bagimlilik/gelistirme/sinama-kosucu.js": metin("sinama-kosucu", 2100),
    "bagimlilik/gelistirme/derleme-eklentisi.js": metin("derleme-eklentisi", 1600),
    "bagimlilik/kosum/csv.js": metin("csv", 780), "bagimlilik/kosum/kuyruk.js": metin("kuyruk-bag", 640) }),
  "yapiyi-uret": (gorunum) => ({ "is/dagitim/paket.js": paketle(gorunum) }),
};

export const TEK = `asama     tek
temel     derleme-tabani
kopyala   kaynak  /is/kaynak
calistir  bagimlilik-kur
calistir  yapiyi-uret
giris     /is/dagitim/paket.js`;

export const COK = `asama     derleyici
temel     derleme-tabani
kopyala   kaynak  /is/kaynak
calistir  bagimlilik-kur
calistir  yapiyi-uret
asama     cikti
temel     kosum-tabani
kopyala   derleyici:/is/dagitim  /uygulama
giris     /uygulama/paket.js`;

// Ayni cok asamali tanim, aradan tum is dizinini tasiyan biciminde.
export const COK_GENIS = COK.replace("derleyici:/is/dagitim  /uygulama", "derleyici:/is  /uygulama")
  .replace("giris     /uygulama/paket.js", "giris     /uygulama/dagitim/paket.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("imaj", { 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();

// Kopyalanacak ciftler: agactan ya da baska bir asamanin gorunumunden.
const kopyaCiftleri = (kaynak, hedef, asamalar) => {
  if (!kaynak.includes(":")) return gez(kaynak).map((y) =>
    [`${hedef.slice(1)}/${y.slice(kaynak.length + 1)}`, readFileSync(`${KOK}/${y}`).toString()]);
  const [ad, yol] = kaynak.split(":");
  const onek = `${yol.slice(1)}/`;
  return [...asamalar.get(ad).gorunum].filter(([y]) => y.startsWith(onek))
    .map(([y, i]) => [`${hedef.slice(1)}/${y.slice(onek.length)}`, i]);
};

export const derle = (tanim, onbellek = new Map()) => {
  const asamalar = new Map();
  let simdi = null, anahtar = "";
  const sayac = { uretim: 0, isabet: 0, bayt: 0 };
  for (const ham of tanim.trim().split("\n")) {
    const [yonerge, kaynak, hedef] = ham.trim().split(/\s+/);
    if (yonerge === "asama") {
      simdi = { ad: kaynak, gorunum: new Map(), katman: 0 };
      asamalar.set(kaynak, simdi); anahtar = ""; continue;
    }
    if (yonerge === "giris") continue; // ustveri: katman uretmez
    const girdi = yonerge === "kopyala" ? kopyaCiftleri(kaynak, hedef, asamalar).join() : "";
    anahtar = ozet(anahtar + ham + girdi);
    let ciftler;
    if (onbellek.has(anahtar)) { ciftler = onbellek.get(anahtar); sayac.isabet++; }
    else {
      ciftler = yonerge === "kopyala" ? kopyaCiftleri(kaynak, hedef, asamalar)
        : Object.entries(URETIM[kaynak](simdi.gorunum));
      for (const [y, i] of ciftler) yazDosya(`imaj/${simdi.ad}/kat-${simdi.katman}/${y}`, i);
      onbellek.set(anahtar, ciftler);
      sayac.uretim++; sayac.bayt += ciftler.reduce((t, [, i]) => t + i.length, 0);
    }
    simdi.katman++;
    for (const [y, i] of ciftler) simdi.gorunum.set(y, i);
  }
  return { asamalar, sayac, son: simdi };
};

// Kosumda gereken: kosum tabani dosyalari ve paketlenmis cikti.
export const gerekli = (yol) => yol.startsWith("kok/") || yol.endsWith("paket.js");
```

## İki Derleme, Aynı Uygulama

İki tanım da aynı kaynaktan aynı paketi üretir. Ölçülen şey, paketin yanında ne kaldığıdır.

```js
// olcum-agi/olcimaj.mjs — tek asamali ve cok asamali derlemenin son imajlarini karsilastirir
import { kurAgac, derle, gerekli, TEK, COK, COK_GENIS } from "./asama.mjs";

const bak = (etiket, tanim) => {
  kurAgac();
  const { asamalar, sayac, son } = derle(tanim);
  const dosyalar = [...son.gorunum];
  const gereksiz = dosyalar.filter(([y]) => !gerekli(y));
  const bayt = dosyalar.reduce((t, [, i]) => t + i.length, 0);
  const ara = [...asamalar.values()].filter((a) => a !== son);
  console.log(etiket.padEnd(20) + String(son.katman).padStart(4) + String(dosyalar.length).padStart(7) +
    String(bayt).padStart(9) + String(gereksiz.length).padStart(9) +
    String(gereksiz.reduce((t, [, i]) => t + i.length, 0)).padStart(9) + String(sayac.bayt).padStart(10) +
    String(ara.reduce((t, a) => t + a.katman, 0)).padStart(6));
  return { bayt, dosyalar, gereksiz };
};

console.log("derleme".padEnd(20) + " kat  dosya     bayt  gereksiz     bayt   uretilen ara-kat");
const t = bak("tek asamali", TEK);
const c = bak("cok asamali", COK);
bak("cok asamali, genis", COK_GENIS);
console.log(`\nson imaj: ${t.bayt} -> ${c.bayt} bayt, ${(t.bayt / c.bayt).toFixed(1)} kat kucuk`);
console.log(`son imajdaki dosya: ${t.dosyalar.length} -> ${c.dosyalar.length}`);
console.log(`kosumda gerekmeyen dosya: ${t.gereksiz.length} -> ${c.gereksiz.length}`);
console.log("tek asamalidaki gereksiz dosyalar: " + t.gereksiz.map(([y]) => y).join(" "));
```

```
derleme              kat  dosya     bayt  gereksiz     bayt   uretilen ara-kat
tek asamali            4     14   237174       11   208222    237174     0
cok asamali            2      3    28952        0        0    266126     4
cok asamali, genis     2      7    35044        4     6092    272218     4

son imaj: 237174 -> 28952 bayt, 8.2 kat kucuk
son imajdaki dosya: 14 -> 3
kosumda gerekmeyen dosya: 11 -> 0
tek asamalidaki gereksiz dosyalar: arac/derleyici.js arac/baglayici.js arac/basliklar.js is/kaynak/giris.js is/kaynak/kuyruk.js is/kaynak/sinama/topla.sinama.js is/kaynak/topla.js bagimlilik/gelistirme/sinama-kosucu.js bagimlilik/gelistirme/derleme-eklentisi.js bagimlilik/kosum/csv.js bagimlilik/kosum/kuyruk.js
```

Son imaj **237 174 bayttan 28 952 bayta**, dosya sayısı **14'ten 3'e**, katman sayısı **4'ten
2'ye** iner. Tek aşamalı imajın **11 dosyası ve 208 222 baytı** koşumda hiç çalıştırılmaz —
imajın yüzde 87,8'i. Bunların arasında üç derleme aracı, dört kaynak dosyası (biri sınama
dosyası), iki geliştirme bağımlılığı ve pakete zaten girmiş iki koşum bağımlılığı vardır. Koşum
bağımlılıklarının iki kez taşınması ayrıca sayılır: hem paketin içinde hem de yanında dururlar.

Katman sayısının düşmesi de ayrı okunmalıdır. Çok aşamalı imajın iki katmanı vardır çünkü son
aşama yalnız iki yönerge çalıştırır; bu, önceki dersin ölçtüğü paylaşım kazancını da değiştirir.
Aynı tabandan türeyen başka servisler o iki katmanın **birincisini** paylaşabilir, ikincisi ise
servise özeldir. Tek aşamalı imajda paylaşılabilecek taban, derleme araçlarını da içerdiği için
hem daha büyüktür hem de derleme aracı sürümü değiştiğinde geçersizleşir — paylaşımın kazancı
değişme sıklığına bağlıdır ve derleme tabanı koşum tabanından daha sık değişir.

Ortamdan çıkarılan fark bu dersin en büyük kalemidir. Derleyici, bağlayıcı ve başlık dosyaları
koşum ortamından tümüyle çıkar; koşan konteynerin dosya sisteminde bir derleme aracı bulunmaz.
Yönerge düzeyinde bunun karşılığı iki satırdır — ikinci bir `asama` ve aşamalar arası tek bir
`kopyala`.

Üçüncü satır kazancın nereden geldiğini ayırır. Aynı çok aşamalı tanım, aradan `/is/dagitim`
yerine tüm `/is` dizinini taşıdığında son imaj **35 044 bayta** ve **7 dosyaya** çıkar; dördü
koşumda gerekmeyen kaynak dosyasıdır ve **6 092 bayt** tutar. Kazancın tamamı ikinci bir aşama
açmaktan gelmez; **taşınan yolun darlığından** gelir. İkinci aşama, birinci aşamanın neyini
almayacağına karar verildiği ölçüde işe yarar. Bu, önceki derste ölçülen dizin kopyalama
sorununun aşama sınırındaki biçimidir: `kopyala` bir yol alır, o yolun altında ne olduğunu
saymaz.

## Çok Aşamalının Bedeli

Ayrım bedava değildir. Aynı iki tanım bir önbellekle iki kez koşturulur: ilk derleme soğuk
önbellekle, ikincisi tek bir kaynak dosyasına satır eklendikten sonra. **IM23.** Değişiklik
`kaynak/topla.js` dosyasına tek satır ekler. **IM24.** Aşama görünümü bellekte tutulur,
katmanlar diske yazılır; ölçülen baytlar gerçek dosya baytlarıdır.

```js
// olcum-agi/bedel.mjs — cok asamali derlemenin bedeli: ara katman ve onbellek davranisi
import { kurAgac, degistir, derle, TEK, COK } from "./asama.mjs";

const dizi = (etiket, tanim) => {
  kurAgac();
  const onbellek = new Map();
  const ilk = derle(tanim, onbellek).sayac;
  degistir("kaynak/topla.js", 1);
  const { asamalar, sayac, son } = derle(tanim, onbellek);
  const ara = [...asamalar.values()].filter((a) => a !== son)
    .reduce((t, a) => t + [...a.gorunum.values()].reduce((u, i) => u + i.length, 0), 0);
  const onbellekBayt = [...onbellek.values()].reduce((t, c) => t + c.reduce((u, [, i]) => u + i.length, 0), 0);
  console.log(etiket.padEnd(14) + String(ilk.uretim).padStart(4) + String(ilk.bayt).padStart(9) +
    String(sayac.isabet).padStart(8) + String(sayac.uretim).padStart(8) + String(sayac.bayt).padStart(9) +
    String(onbellek.size).padStart(8) + String(onbellekBayt).padStart(9) + String(ara).padStart(10));
};

console.log("derleme".padEnd(14) + " ilk  ilk-bayt  isabet  uretim     bayt  onbel.     bayt gonderilmez");
dizi("tek asamali", TEK);
dizi("cok asamali", COK);
```

```
derleme        ilk  ilk-bayt  isabet  uretim     bayt  onbel.     bayt gonderilmez
tek asamali      4   237174       1       3   134792       7   371966         0
cok asamali      6   266126       2       4   158018      10   424144    237202
```

Bedel dört kalemdir. Birincisi ilk derlemedir: çok aşamalı düzen **6 katman ve 266 126 bayt**
üretir, tek aşamalı **4 katman ve 237 174 bayt** — fark 28 952 bayt, tam olarak ikinci aşamanın
kendi tabanı ve taşınan paket kadar. İkincisi kaynak değişikliğidir: tek aşamalı 3 katman ve
134 792 bayt yeniden üretirken çok aşamalı **4 katman ve 158 018 bayt** üretir, çünkü paket
değiştiğinde aşamalar arası kopyalama da geçersizleşir. Üçüncüsü önbellektir: 7 girdi ve
371 966 bayta karşı **10 girdi ve 424 144 bayt**.

Dördüncü kalem en görünmezidir. Çok aşamalı düzende her derlemede **237 202 bayt** üretilir ve
hiçbir imaja girmez. Ara aşamanın katmanları gönderilmez ama üretilir; derleyen makinenin
diskinde durur, önbellekte yer kaplar ve bir sonraki derlemede yeniden kullanılır. **İmaj küçüldü,
depo küçülmedi.** Yalıtım bütçesi bu dersteki hesabını böyle kapatır: koşum ortamından 208 222
bayt çıkarıldı, karşılığında derleme ortamında 237 202 baytlık kalıcı bir yığın ve derleme başına
yüzde 17 daha fazla yazma işi doğdu.

Bu dört kalem karara nasıl çevrilir? İki sayı yan yana konur. Fazladan bedel **derleme başına**
ödenir ve 28 952 bayttır. Kazanç ise imajın **her kopyası başına** ödenir ve 208 222 bayttır: imaj
depoya yazıldığında, bir makineye çekildiğinde ve diskte durduğunda o kadar bayt eksiktir. Oran
7,2'dir — tek bir derlemenin ürettiği tek bir imaj bile fazladan bedeli fazlasıyla karşılar. Kurgu
ölçüm ağının dört servisi düşünüldüğünde fark çarpılır, çünkü fazladan bedel her derlemede bir
kez, kazanç ise her saklama ve her çekme işleminde yeniden ortaya çıkar. Tek aşamalı düzenin
savunulabildiği yer, imajın hiç saklanmadığı ve tek bir makinede üretilip orada çalıştırıldığı
durumdur; bunun dışında ölçü tek yönü gösterir.

Aşama sınırının ne olmadığı da yazılmalıdır. `kopyala derleyici:/is/dagitim` satırı bir kopyalama
sınırıdır, bir doğrulama sınırı değil. Son aşama, önceki aşamanın ürettiği paketi olduğu gibi
alır; içinde ne olduğunu sormaz. Paketleyici sınama dosyalarını dışarıda bırakmasaydı onlar da
son imaja girerdi ve ölçüm bunu yakalamazdı — çünkü `gerekli` tanımı bu ölçümde **elle**
yazılmıştır. İmajın kendisi neyin gerektiğini bilmez; taşınan yolun doğru seçildiğini yazan kişi
garanti eder. Önceki derste ölçülen eksikliğin buradaki karşılığı budur: yönerge neyin
gerektiğini değil, neyin kopyalandığını kaydeder.

## Özet

- Çok aşamalı derleme, derleme zamanında gereken dosyaları koşum zamanında gerekenlerden ayırır:
  derleme kendi aşamasında yapılır, son imaja yalnız üretilen dosyalar taşınır.
- Aynı uygulama iki biçimde derlendiğinde son imaj 237 174 bayttan 28 952 bayta indi (8,2 kat),
  dosya sayısı 14'ten 3'e, katman sayısı 4'ten 2'ye düştü.
- Tek aşamalı imajın 11 dosyası ve 208 222 baytı koşumda hiç çalıştırılmıyordu — imajın yüzde
  87,8'i. Üç derleme aracı, dört kaynak dosyası, iki geliştirme bağımlılığı ve pakete zaten girmiş
  iki koşum bağımlılığı.
- Bedel dört kalemdir: ilk derlemede 28 952 bayt fazla üretim, kaynak değişikliğinde 3 yerine 4
  katman ve 134 792 yerine 158 018 bayt, önbellekte 7 yerine 10 girdi, ve her derlemede üretilip
  hiçbir imaja girmeyen 237 202 bayt. İmaj küçüldü, derleyen makinedeki depo küçülmedi.
- Kazanç ikinci aşamayı açmaktan değil, taşınan yolun darlığından gelir: aradan `/is/dagitim`
  yerine tüm `/is` taşındığında son imaj 35 044 bayta ve 7 dosyaya çıktı, 4 gereksiz dosya ve
  6 092 bayt geri geldi.
- Aşama sınırı bir kopyalama sınırıdır, doğrulama sınırı değil. Son aşama önceki aşamanın
  ürettiğini olduğu gibi alır; koşumda neyin gerektiği tanımı yönergenin dışında, yazan kişinin
  elindedir.

## Sonraki Adım

Son imajın üç dosyası kaldı ve ikisi tabandan geliyor: koşum tabanı 5 740 bayt, paket 23 212 bayt.
Derlemenin ürettiği her şey ölçülüp küçültüldükten sonra geriye, imajın **üretilmemiş** bölümü
kalır — altına konan taban. Bu ders boyunca taban bir sabitti; koşum tabanının hangi dosyaları
taşıdığı, hangi kabuğu ve hangi kütüphaneleri içerdiği hiç sorulmadı. Oysa taban son imajın beşte
birini oluşturuyor ve içeriği yazan kişinin ürettiği bir şey değil, dışarıdan alınan bir şey.
Sonraki ders bu seçimi ölçer: küçük bir taban ile geniş bir taban arasındaki fark kaç bayt ve kaç
dosyadır, küçük taban neyi eksik bırakır ve o eksiklik hangi durumda ödenir?
