İçeriğe geç
academia.sh

Ders 07 / 21

İmaj Tanım Dosyası

Katmanların elle değil yönergelerden doğduğu ölçülür: beş yönergelik bir tanım dosyası biçimi tanımlanır, yönergeleri sırayla uygulayan bir yorumlayıcı yazılır ve derleme bağlamının büyüklüğü sayılır — ağaca giren dosya ve bayt, dışlama kuralının kestiği pay ve bağlamda olup imaja hiç girmeyen dosyalar.

İçindekiler

Önceki dersin katmanları elle tanımlanmıştı: hangi dosyanın hangi katmana gireceği bir nesne sabitinde yazılıydı. Bu, ölçümü yapmaya yetiyordu ama bir imajın nasıl kurulduğunu anlatmıyordu. Gerçek bir imaj, sırayla uygulanan yönergelerden doğar; her yönerge bir öncekinin üstüne bir katman koyar ve sonuncusu bittiğinde imaj hazırdır.

Bu ders o yönergeleri ve yönergelerin beslendiği kümeyi ölçer. Kopyalanacak dosyalar bir yerden gelir: yönergelerin okuyabileceği dosya kümesine derleme bağlamı denir ve bağlamın büyüklüğü bir bedeldir. Ölçülecek üç sayı vardır — bağlama kaç dosya ve kaç bayt girer, dışlama kuralı kaç dosyayı ve kaç baytı keser, bağlama girip imaja hiç ulaşmayan kaç dosya kalır?

IM7. Aşağıdaki yönerge kümesi ve tanım dosyası biçimi bu ders için tanımlanmıştır; beş yönergeden oluşur ve gerçek imaj tanım dosyası biçimlerinden çok daha küçüktür. Yorumlayıcı node ile yazılan bir modeldir. IM8. calistir yönergesi gerçek bir komut çalıştırmaz; modellenmiş bir komut tablosundan dosya üretir — bağımlılık kurulumunun çıktısı böyle temsil edilir. IM9. Ağaç, kurgudaki bölgesel ölçüm ağının okuma toplayıcı servisinin kaynak ağacıdır; dosya içerikleri satır sayısıyla ölçeklenmiş üretilmiş metindir. IM10. Dışlama kuralı önek eşleşmesiyle çalışır; gerçek kurallar desen dili kullanır, bu ölçümün sayılarını değiştirmez.

Beş Yönerge

Tanım dosyası bir metin dosyasıdır ve her satırı bir yönergedir. Yönergeler sırayla uygulanır; sıra, önceki dersin yığın sırasının aynısıdır — altta duran önce çalışır.

Yönerge Ne yapar Katman üretir mi
temel Yığının en altına bir başlangıç dosya kümesi koyar evet
kopyala Derleme bağlamından bir yolu imajın içine kopyalar evet
calistir Bir komut çalıştırıp ürettiği dosyaları katman yapar evet
ayar İmajın üstverisine bir değer yazar hayır
giris Konteyner başladığında çalışacak yolu üstveriye yazar hayır

Son iki yönerge dosya sistemine dokunmaz; imajın üstverisini yazar. Ayrım ölçülecektir: bir imajın katman sayısı, tanım dosyasının satır sayısı değildir.

Derleme Bağlamı

Ağaç diske kurulur ve dışlama kuralının ne kestiği sayılır. Bağlam, yönergelerin okuyabileceği kümedir; dışlama kuralına takılan bir yol, hiçbir yönerge onu istemese bile okunmaz.

// olcum-agi/proje.mjs — kurgu servisin kaynak agacini diske kurar, derleme baglamini olcer
import { mkdirSync, writeFileSync, readdirSync, readFileSync, rmSync } from "node:fs";

export const KOK = "proje";
export const metin = (ad, satir) => Array.from({ length: satir }, (_, i) => `// ${ad} ${i}\n`).join("");

export const TANIM = `temel     kosum-tabani
kopyala   ayar/bolge.json  /uygulama/ayar/bolge.json
calistir  bagimlilik-kur
kopyala   kaynak           /uygulama/kaynak
ayar      BOLGE=kuzey
giris     /uygulama/kaynak/giris.js
`;

const AGAC = {
  "imaj-tanimi": TANIM,
  "ayar/bolge.json": '{"bolge":"kuzey","sayac":41820}\n',
  "ayar/yerel.json": '{"kapi":8080,"kutuk":"ayrintili"}\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-topla", 210),
  "kaynak/sinama/kuyruk.sinama.js": metin("sinama-kuyruk", 160),
  "bagimlilik/csv/dizin.js": metin("csv", 900),
  "bagimlilik/kuyruk/dizin.js": metin("kuyruk-bag", 1400),
  "belge/mimari.md": metin("belge", 240),
  "gunluk/kosum-1873.log": metin("gunluk", 2600),
  "yapi-artigi/paket-onceki.js": metin("artik", 1100),
  ".yerel-ayar/duzenleyici.json": metin("duzenleyici", 12),
  "okuma-ornekleri/gun-1.csv": metin("ornek", 380),
};

// Dislama kurali: bu oneklerle baslayan yollar baglama hic alinmaz.
export const DISLAMA = ["bagimlilik/", "gunluk/", "yapi-artigi/", ".yerel-ayar/", "belge/"];

export const kurAgac = () => {
  rmSync(KOK, { recursive: true, force: true });
  for (const [yol, icerik] of Object.entries(AGAC)) {
    mkdirSync(`${KOK}/${yol}`.split("/").slice(0, -1).join("/"), { recursive: true });
    writeFileSync(`${KOK}/${yol}`, icerik);
  }
};

export const gez = (kok, alt = "") =>
  readdirSync(`${kok}${alt}`, { withFileTypes: true }).flatMap((g) =>
    g.isDirectory() ? gez(kok, `${alt}/${g.name}`) : [`${alt}/${g.name}`.slice(1)]).sort();

export const bayt = (yol) => readFileSync(`${KOK}/${yol}`).length;
export const disarida = (yol, kural = DISLAMA) => kural.some((d) => yol.startsWith(d));
export const baglam = (kural = DISLAMA) => gez(KOK).filter((y) => !disarida(y, kural));
export const toplam = (yollar) => yollar.reduce((t, y) => t + bayt(y), 0);
// olcum-agi/olcbaglam.mjs — agac, dislama ve baglam buyuklugu
import { kurAgac, gez, bayt, disarida, baglam, toplam, KOK, TANIM } from "./proje.mjs";

kurAgac();
const tumu = gez(KOK);
const kesilen = tumu.filter((y) => disarida(y));
console.log("imaj tanimi:\n" + TANIM);
console.log("ust dizin".padEnd(16) + "dosya   bayt   baglamda");
const grup = {};
for (const y of tumu) (grup[y.includes("/") ? y.split("/")[0] + "/" : "(kok)"] ??= []).push(y);
for (const [d, yollar] of Object.entries(grup))
  console.log(d.padEnd(16) + String(yollar.length).padStart(3) + String(toplam(yollar)).padStart(8) +
    "   " + (disarida(yollar[0]) ? "hayir" : "evet"));
console.log(`\nagac toplami    : ${tumu.length} dosya, ${toplam(tumu)} bayt`);
console.log(`dislama kesti   : ${kesilen.length} dosya, ${toplam(kesilen)} bayt`);
console.log(`derleme baglami : ${baglam().length} dosya, ${toplam(baglam())} bayt ` +
  `(agacin %${((toplam(baglam()) * 100) / toplam(tumu)).toFixed(1)} bayti)`);
imaj tanimi:
temel     kosum-tabani
kopyala   ayar/bolge.json  /uygulama/ayar/bolge.json
calistir  bagimlilik-kur
kopyala   kaynak           /uygulama/kaynak
ayar      BOLGE=kuzey
giris     /uygulama/kaynak/giris.js

ust dizin       dosya   bayt   baglamda
.yerel-ayar/      1     206   hayir
ayar/             2      66   evet
bagimlilik/       2   35280   hayir
belge/            1    3010   hayir
gunluk/           1   37890   hayir
(kok)             1     203   evet
kaynak/           5   10505   evet
okuma-ornekleri/  1    4830   evet
yapi-artigi/      1   14290   hayir

agac toplami    : 15 dosya, 106280 bayt
dislama kesti   : 6 dosya, 90676 bayt
derleme baglami : 9 dosya, 15604 bayt (agacin %14.7 bayti)

Ağaç 15 dosya ve 106 280 bayttır; dışlama kuralı 6 dosya ve 90 676 baytı keser, bağlama 9 dosya ve 15 604 bayt kalır — ağacın bayt olarak yüzde 14,7’si. Kesilen payın büyüklüğü tesadüf değildir: bir kaynak ağacının hacminin çoğu kurulu bağımlılıklar, kütükler ve önceki yapı artıklarıdır. Dışlama kuralı yazılmazsa bu 90 676 bayt her derlemede okunur ve bir sonraki dersin ölçeceği önbellek kararına da girer.

Kuralın atladığı bir şey de sayıdadır: okuma-ornekleri/ dizini bağlamda kalmıştır ve 4 830 bayt taşır. Dışlama kuralı yalnız yazıldığı yolları keser; yazılmayan her yol bağlamdadır.

Yorumlayıcı

Yorumlayıcı tanım dosyasını satır satır okur, her satır için ayrı bir katman dizini açar ve yönergeyi o dizine uygular. Kopyalanan yolların kaydı tutulur; ölçümün sonundaki fark buradan çıkar.

// olcum-agi/yorumla.mjs — yonergeleri sirayla uygular, her yonergeden bir katman uretir (model)
import { mkdirSync, writeFileSync, readFileSync, rmSync, statSync } from "node:fs";
import { gez, disarida, metin, DISLAMA, KOK } from "./proje.mjs";

const TABAN = { "kok/kabuk.js": metin("kabuk", 60), "kok/kosum.js": metin("kosum", 420) };
const KOMUT = { "bagimlilik-kur": { "uygulama/bagimlilik/csv.js": metin("csv-kurulu", 900),
  "uygulama/bagimlilik/kuyruk.js": metin("kuyruk-kurulu", 1400) } };
const yaz = (dizin, dosyalar) => {
  for (const [yol, icerik] of Object.entries(dosyalar)) {
    mkdirSync(`${dizin}/${yol}`.split("/").slice(0, -1).join("/"), { recursive: true });
    writeFileSync(`${dizin}/${yol}`, icerik);
  }
};

export const derle = (tanim, kural = DISLAMA) => {
  rmSync("imaj", { recursive: true, force: true });
  const okunan = new Set(), ustveri = {}, katmanlar = [];
  tanim.trim().split("\n").forEach((satir, i) => {
    const [yonerge, ...arg] = satir.trim().split(/\s+/);
    const dizin = `imaj/kat-${i}`;
    mkdirSync(dizin, { recursive: true });
    if (yonerge === "temel") yaz(dizin, TABAN);
    else if (yonerge === "calistir") yaz(dizin, KOMUT[arg[0]]);
    else if (yonerge === "kopyala") {
      const [kaynak, hedef] = arg;
      const tekil = !statSync(`${KOK}/${kaynak}`).isDirectory();
      const bas = tekil || kaynak === "." ? "" : `${kaynak}/`;
      const alt = (tekil ? [kaynak] : gez(KOK, bas ? `/${kaynak}` : "")).filter((y) => !disarida(y, kural));
      for (const y of alt) {
        const ic = `${dizin}/${hedef.slice(1)}` + (tekil ? "" : `/${y.slice(bas.length)}`);
        mkdirSync(ic.split("/").slice(0, -1).join("/"), { recursive: true });
        writeFileSync(ic, readFileSync(`${KOK}/${y}`));
        okunan.add(y);
      }
    } else ustveri[yonerge] = arg.join(" ");
    const yollar = gez(dizin);
    katmanlar.push({ sira: i, yonerge, arg: arg.join(" "), dosya: yollar.length,
      bayt: yollar.reduce((t, y) => t + statSync(`${dizin}/${y}`).size, 0) });
  });
  return { katmanlar, okunan, ustveri };
};
// olcum-agi/olcyorum.mjs — yorumlayiciyi kosturur, baglam ile imaj arasindaki farki sayar
import { derle } from "./yorumla.mjs";
import { kurAgac, baglam, toplam, TANIM } from "./proje.mjs";

kurAgac();
const { katmanlar, okunan, ustveri } = derle(TANIM);
console.log("sira yonerge   arguman".padEnd(58) + "tur          dosya   bayt");
for (const k of katmanlar)
  console.log(`  ${k.sira}  ${k.yonerge.padEnd(9)} ${k.arg}`.padEnd(58) +
    (k.dosya ? "dosya sistemi" : "ustveri      ") + String(k.dosya).padStart(4) + String(k.bayt).padStart(8));

const dosyaKatmani = katmanlar.filter((k) => k.dosya > 0);
const imajBayt = dosyaKatmani.reduce((t, k) => t + k.bayt, 0);
const girmeyen = baglam().filter((y) => !okunan.has(y));
const sinama = [...okunan].filter((y) => y.includes("/sinama/"));
console.log(`\nustveri: ${JSON.stringify(ustveri)}`);
console.log(`imaj: ${dosyaKatmani.length} dosya sistemi katmani, ${katmanlar.length - dosyaKatmani.length} ustveri yonergesi, ${imajBayt} bayt`);
console.log(`baglamdan okunan ${okunan.size} dosya, ${toplam([...okunan])} bayt`);
console.log(`baglamda olup imaja hic girmeyen ${girmeyen.length} dosya, ${toplam(girmeyen)} bayt: ${girmeyen.join(" ")}`);
console.log(`imaja giren ama kosumda gerekmeyen ${sinama.length} dosya, ${toplam(sinama)} bayt`);
console.log(`derleme baglami ${toplam(baglam())} bayt okundu, imaja ${toplam([...okunan])} bayt gecti`);
sira yonerge   arguman                                    tur          dosya   bayt
  0  temel     kosum-tabani                               dosya sistemi   2    6060
  1  kopyala   ayar/bolge.json /uygulama/ayar/bolge.json  dosya sistemi   1      32
  2  calistir  bagimlilik-kur                             dosya sistemi   2   45780
  3  kopyala   kaynak /uygulama/kaynak                    dosya sistemi   5   10505
  4  ayar      BOLGE=kuzey                                ustveri         0       0
  5  giris     /uygulama/kaynak/giris.js                  ustveri         0       0

ustveri: {"ayar":"BOLGE=kuzey","giris":"/uygulama/kaynak/giris.js"}
imaj: 4 dosya sistemi katmani, 2 ustveri yonergesi, 62377 bayt
baglamdan okunan 6 dosya, 10537 bayt
baglamda olup imaja hic girmeyen 3 dosya, 5067 bayt: ayar/yerel.json imaj-tanimi okuma-ornekleri/gun-1.csv
imaja giren ama kosumda gerekmeyen 2 dosya, 7340 bayt
derleme baglami 15604 bayt okundu, imaja 10537 bayt gecti

Altı satırlık tanım dosyası dört dosya sistemi katmanı üretir; iki satır yalnız üstveri yazar ve yığına hiçbir bayt eklemez. Önceki dersin ölçüsüyle söylenirse: ayar ve giris satırlarının değiştirilmesi hiçbir katmanın içerik özetini değiştirmez, çünkü ortada katman yoktur.

Bağlama Girip İmaja Girmeyen

Üç sayı yan yana konduğunda tanım dosyasının asıl maliyeti görünür. Bağlam 15 604 bayttır, bağlamdan okunan 10 537 bayttır, imaj 62 377 bayttır. Üçü de farklıdır ve farkların her biri ayrı bir şey söyler.

Bağlam ile okunan arasındaki fark 3 dosya ve 5 067 bayttır: yerel ayar dosyası, tanım dosyasının kendisi ve örnek okuma verisi bağlama girer, hiçbir yönerge onları istemez, imaja hiç ulaşmazlar. Yine de derleme adımının okuma erişimindedirler — bağlamda olmak, imaja girmekten bağımsız bir izindir. Bu, dışlama kuralının yalnız boyut için yazılmadığını gösterir.

Okunan ile imaj arasındaki fark ters yöndedir: imaj bağlamdan aldığından beş kat büyüktür. Farkın kaynağı calistir yönergesidir; tek satır 45 780 bayt üretir, imajın yüzde 73’ü o katmandır. Dışlama kuralının kestiği 35 280 baytlık bağımlılık ağacı imajdan çıkmış değildir, kaynağı değişmiştir: yazan kişinin makinesinde kurulmuş hâliyle kopyalanmak yerine yönergenin kendisi tarafından üretilir. Ortamdan çıkarılan fark budur — kurulu bağımlılıkların sürümü artık bir makinenin durumu değil, tanım dosyasının bir satırıdır.

Bedelin son kalemi kopyala kaynak satırındadır. Bir dizin kopyalandığında içine ne girdiğini yönerge söylemez: sınama dosyaları da imaja girer, 2 dosya ve 7 340 bayt, imajın koşumda gerekmeyen yüzde 11,8’i. Yönerge dosya değil dizin adı taşıdığı için bu, tanım dosyasına bakarak görülemez; ancak katman içeriği sayılarak görülür. Yalıtımın eksik kaldığı yer burasıdır: imaj neyin gerektiğini değil, neyin kopyalandığını kaydeder.

Seçici Kopyalama ile Bağlamı Kopyalamak

Yönergenin ne kadarını seçtiği bir karardır ve karar üç biçimde sayılabilir. IM11. İkinci bir tanım dosyası tüm bağlamı tek yönergeyle kopyalar; üçüncü ölçüm aynı tanımı dışlama kuralı boşken koşturur. IM12. Koşumda gereken dosyalar açıkça listelenir: sınama dizini dışındaki kaynak dosyaları ve bölge ayarı. Geri kalan her şey imaja girdiyse gereksizdir.

// olcum-agi/genis.mjs — secici kopyalama ile tum baglami kopyalamayi karsilastirir
import { derle } from "./yorumla.mjs";
import { kurAgac, toplam, TANIM } from "./proje.mjs";

const GENIS = `temel     kosum-tabani
kopyala   .  /uygulama
calistir  bagimlilik-kur
giris     /uygulama/kaynak/giris.js
`;

const GEREKLI = (y) => (y.startsWith("kaynak/") && !y.includes("/sinama/")) || y === "ayar/bolge.json";

kurAgac();
const olc = (etiket, tanim, kural) => {
  const { katmanlar, okunan } = derle(tanim, kural);
  const k = katmanlar.filter((x) => x.dosya > 0);
  const gereksiz = [...okunan].filter((y) => !GEREKLI(y));
  console.log(etiket.padEnd(30) + String(k.length).padStart(2) + String(k.reduce((t, x) => t + x.dosya, 0)).padStart(7) +
    String(k.reduce((t, x) => t + x.bayt, 0)).padStart(9) + String(gereksiz.length).padStart(9) +
    String(toplam(gereksiz)).padStart(8));
};
console.log("tanim".padEnd(30) + "kat  dosya     bayt  gereksiz   bayt");
olc("secici kopyalama", TANIM);
olc("tum baglam, dislama var", GENIS);
olc("tum baglam, dislama yok", GENIS, []);
tanim                         kat  dosya     bayt  gereksiz   bayt
secici kopyalama               4     10    62377        2    7340
tum baglam, dislama var        3     13    67444        5   12407
tum baglam, dislama yok        3     19   158120       11  103083

Üç satır aynı uygulamayı üretir ve boyutları 62 377, 67 444 ve 158 120 bayttır. İlk iki satır arasındaki fark küçüktür — 5 067 bayt — çünkü dışlama kuralı zaten ağır dizinleri kesmiştir. Üçüncü satırda kural boşaltıldığında imaj 2,5 kat büyür ve içindeki gereksiz dosya sayısı ikiden on bire, gereksiz bayt 7 340’tan 103 083’e çıkar. İmajın üçte ikisi çalıştırılmayacak dosyalardan oluşur: kütükler, önceki yapı artıkları, belgeler ve yerel ayar.

İki tanımın katman sayısı da farklıdır: seçici tanım dört, geniş tanım üç dosya sistemi katmanı üretir. Daha az katman burada iyi bir sonuç değildir — geniş tanımda kaynak ile ayar aynı yönergeye girdiği için ayrı katmanlara ayrılamamıştır, üstelik bağımlılık kurulumundan önce kopyalanmıştır. Bu iki ayrıntının bedeli bu derste görünmez; sonraki dersin ölçtüğü şey tam olarak odur.

Özet

  • İmaj tanım dosyası bir yönerge dizisidir; yönergeler sırayla uygulanır ve her biri bir öncekinin üstüne katman koyar. Altı satırlık tanım dört dosya sistemi katmanı üretti, iki satır yalnız üstveri yazdı — katman sayısı satır sayısı değildir.
  • Derleme bağlamı, yönergelerin okuyabileceği dosya kümesidir. 15 dosyalık 106 280 baytlık ağaçtan dışlama kuralı 6 dosya ve 90 676 baytı kesti; bağlama 9 dosya ve 15 604 bayt kaldı.
  • Bağlamda olup imaja hiç girmeyen 3 dosya ve 5 067 bayt sayıldı. Bağlamda olmak imaja girmekten ayrı bir izindir; dışlama kuralı yalnız boyut için yazılmaz.
  • calistir yönergesi tek satırda 45 780 bayt üretti — imajın yüzde 73’ü. Dışlanan bağımlılık ağacı imajdan çıkmadı, kaynağı bir makinenin durumundan tanım dosyasının bir satırına geçti.
  • Dizin kopyalayan yönerge 2 sınama dosyasını ve 7 340 baytı imaja soktu. İmaj neyin gerektiğini değil neyin kopyalandığını kaydeder; fark ancak katman içeriği sayılarak görünür.
  • Aynı uygulama üç tanımla derlendiğinde imaj 62 377, 67 444 ve 158 120 bayt çıktı. Dışlama kuralı boşken tüm bağlamı kopyalayan tanım imajı 2,5 katına çıkardı ve içindeki gereksiz dosya sayısını ikiden on bire taşıdı.

Sonraki Adım

Bu ölçüm tek bir derlemeyi saydı. Yönergeler her derlemede baştan uygulandığında calistir satırının ürettiği 45 780 bayt da her seferinde yeniden üretilir — oysa bağımlılık listesi değişmediyse çıktısı da değişmez. Katmanların içerik özetiyle adlandığı bilindiğine göre, değişmemiş bir yönergenin katmanı yeniden üretilmek yerine saklanandan alınabilir. Sonraki ders bu saklamayı kurar ve tek bir kararın sayısını verir: bağımlılık kurulumu ile kaynak kopyalamanın hangi sırada yazıldığı, aynı değişiklik dizisinde önbellek isabet oranını, yeniden üretilen katman sayısını ve kopyalanan toplam baytı ne kadar oynatır?

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat