İçeriğe geç
academia.sh

Ders 19 / 21

Yazılım Malzeme Listesi

Yapı çıktısının içindeki bileşenler bir envanter olarak üretilir ve üç sayıyla ölçülür: envanterin gördüğü doğrudan, geçişli ve derleme zamanı bileşenleri, hiç göremediği üç sınıf, ve bir kusur duyurusu geldiğinde yanıtın kaç sorguya indiği.

İçindekiler

Önceki ders sırrın depoda şifreli durabildiğini ölçtü: anahtar bir yerde, çözebilen özne sayılı, döndürme belli sayıda kaydı yeniden yazıyor. Depoya giren yapılandırma artık bildirilmiştir — ama bildirilen yalnız yapılandırmadır. Hattın ürettiği çıktının içinde ne olduğu hâlâ hiçbir yerde yazmaz.

Bu ders o beyanı üretir. Ölçü üç sayıdır: envanter kaç bileşeni görüyor, kaçını hiç göremiyor, bir kusur duyurusu geldiğinde yanıt kaç sorguya iniyor. M22/K02 taramanın yakaladığı ve kaçırdığı kusurları saymıştı; o sayım tekrarlanmaz. Buradaki nesne envanterin kendisidir — hattın ürettiği bir çıktı olarak.

DD19. Kurgu bölgesel ölçüm ağı dört servistir ve 64 bileşenlik bir havuzu paylaşır; ağaç node ile diske yazılır ve bir modeldir. DD20. Envanter üreteci yalnız beyan dosyalarını okur: bildirim, çözüm ve derleme. DD21. Beyan edilmeyen üç yol vardır — kaynağa kopyalanmış, ikiliye gömülü, koşum anında inen; üretecin tohumu 20260214 görünürdür.

Envanteri Üretmek

// olcum-agi/envanter.mjs — kurgu bagimlilik agaci diske yazilir, envanter uretilir
import { mkdirSync, rmSync, writeFileSync, readFileSync, statSync } from "node:fs";
import { join, dirname } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-envanter");
rmSync(KOK, { recursive: true, force: true });
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(20260214);                                   // tohum gorunur
const sec = (d) => d[Math.floor(r() * d.length)];

// Kurgu bolgesel olcum agi: dort servis. Havuz paylasilir, ayni bilesen birden cok serviste gecer.
const HAVUZ = Array.from({ length: 64 }, (_, i) => `bilesen-${String(i + 1).padStart(2, "0")}`);
const SURUM = ["1.4", "2.0", "2.3", "2.6", "3.1", "3.4"];
const SERVIS = [                        // kopya/gomulu/kosum: beyan edilmeyen bilesenler
  { ad: "toplayici", dogrudan: 9, arac: 5, kopya: 2, gomulu: 1, kosum: 1, ickopya: 1 },
  { ad: "dogrulayici", dogrudan: 7, arac: 4, kopya: 1, gomulu: 1, kosum: 0, ickopya: 1 },
  { ad: "faturalama", dogrudan: 12, arac: 6, kopya: 2, gomulu: 2, kosum: 2, ickopya: 1 },
  { ad: "is-emri", dogrudan: 6, arac: 4, kopya: 1, gomulu: 0, kosum: 1, ickopya: 0 },
];
const yaz = (yol, icerik) => {
  mkdirSync(dirname(yol), { recursive: true });
  writeFileSync(yol, icerik);
};

for (const s of SERVIS) {
  const alinan = new Set();
  const yeni = () => { let a; do { a = sec(HAVUZ); } while (alinan.has(a)); alinan.add(a); return a; };
  const dogrudan = {}, cozum = {}, araclar = {};
  for (let i = 0; i < s.dogrudan; i++) {
    const ad = yeni();
    cozum[ad] = { surum: (dogrudan[ad] = sec(SURUM)), gerektiren: "" };
    for (let k = 0, n = Math.floor(r() * 4); k < n; k++)       // gecisli cocuklar
      cozum[yeni()] = { surum: sec(SURUM), gerektiren: ad };
  }
  for (let i = 0; i < s.arac; i++) araclar[yeni()] = sec(SURUM);
  yaz(join(KOK, s.ad, "bildirim.json"), JSON.stringify({ servis: s.ad, dogrudan }, null, 1));
  yaz(join(KOK, s.ad, "cozum.json"), JSON.stringify(cozum, null, 1));
  yaz(join(KOK, s.ad, "derleme.json"), JSON.stringify({ araclar }, null, 1));

  for (let i = 0; i < s.kopya; i++)                            // kaynaga kopyalanmis ucuncu taraf
    yaz(join(KOK, s.ad, "kaynak", `k${i}.js`), `// kopyalanmis: ${yeni()}@${sec(SURUM)}\n`);
  for (let i = 0; i < s.ickopya; i++)                          // kendi kodu, ucuncu taraf degil
    yaz(join(KOK, s.ad, "kaynak", `i${i}.js`), `// kopyalanmis: [email protected]\n`);
  for (let i = 0; i < s.gomulu; i++)                           // ikilinin icine gomulu
    yaz(join(KOK, s.ad, "cikti", "gomulu", `g${i}.bin`), Buffer.concat(
      [Buffer.from(`GOMULU:${yeni()}@${sec(SURUM)}\n`), Buffer.alloc(2048, "x")]));
  const inen = Array.from({ length: s.kosum }, () => `${yeni()}@${sec(SURUM)}`);
  yaz(join(KOK, s.ad, "cikti", "indirilenler.json"), JSON.stringify({ inen }, null, 1));
  yaz(join(KOK, s.ad, "cikti", "govde.js"), `// ${s.ad} yapi ciktisi\n`);
}

// Envanter ureteci YALNIZ beyan dosyalarini okur; okunan dosya ve bayt sayilir.
let dosya = 0, bayt = 0;
const oku = (y) => { dosya++; bayt += statSync(y).size; return JSON.parse(readFileSync(y)); };
const envanter = [];
for (const s of SERVIS) {
  const bildirim = oku(join(KOK, s.ad, "bildirim.json")).dogrudan;
  const cozum = oku(join(KOK, s.ad, "cozum.json"));
  for (const [ad, k] of Object.entries(cozum))
    envanter.push({ servis: s.ad, ad, surum: k.surum,
      tur: ad in bildirim ? "dogrudan" : "gecisli", gerektiren: k.gerektiren });
  for (const [ad, sur] of Object.entries(oku(join(KOK, s.ad, "derleme.json")).araclar))
    envanter.push({ servis: s.ad, ad, surum: sur, tur: "derleme", gerektiren: "(derleme)" });
}
const yolEnv = join(KOK, "envanter.json");
yaz(yolEnv, JSON.stringify(envanter, null, 1));
yaz(join(KOK, "maliyet.json"), JSON.stringify({ dosya, bayt }));

const say = (t) => envanter.filter((k) => k.tur === t).length;
console.log(`tohum 20260214; servis ${SERVIS.length}, havuz ${HAVUZ.length} bilesen`);
console.log(`envanteri uretmek icin okunan beyan dosyasi ${dosya}, ${bayt} bayt`);
console.log(`envanter kaydi ${envanter.length} -> dogrudan ${say("dogrudan")}, ` +
  `gecisli ${say("gecisli")}, derleme zamani ${say("derleme")}`);
console.log(`envanter dosyasi ${statSync(yolEnv).size} bayt, ` +
  `benzersiz bilesen ${new Set(envanter.map((k) => k.ad)).size}`);
tohum 20260214; servis 4, havuz 64 bilesen
envanteri uretmek icin okunan beyan dosyasi 12, 7124 bayt
envanter kaydi 107 -> dogrudan 34, gecisli 54, derleme zamani 19
envanter dosyasi 12659 bayt, benzersiz bilesen 56

Dört servis toplam 34 doğrudan bağımlılık bildirir. Envanter 107 kayıt üretir: o 34’ün yanına 54 geçişli bileşen ve 19 derleme zamanı aracı gelir. Beyan edilenle taşınan arasındaki oran buradadır — ekip 34 ad yazar, çıktıya 88 bileşen girer. Yalnız doğrudan bağımlılıklara bakan bir kapı, beyan edilen 88 koşum bileşeninin 34’ünü (%39) görür.

Üretimin maliyeti ortamdan bağımsız niceliklerle yazılır: 12 beyan dosyası, 7.124 bayt okuma, 107 kayıt, 12.659 baytlık envanter. Süre basılmaz: süre makineye bağlıdır, okunan dosya sayısı değildir.

107 kaydın arkasında 56 benzersiz bileşen vardır: aynı bileşen birden çok serviste geçer. “Hangi servisler etkileniyor” sorusunun tek bir dosyadan yanıtlanabilmesi buna dayanır.

Envanterin Görmediği

Üreteç beyan dosyalarını okur. Beyan dosyası niyeti anlatır: neyin çekilmesi istendiğini. Çıktı ağacı ise sonucu taşır. İkisi aynı olmak zorunda değildir.

DD22. Derleme zamanı araçları üretimde koşmaz; koşan bileşen kümesine girmez. DD23. Denetçi ağacı gezer ve kopyalanmis: ya da GOMULU: başlığı taşıyan her dosyayı beyan dışı sayar; kendi kodunun kopyası da aynı başlığı taşır.

// olcum-agi/kapsam.mjs — envanterin gormedigi: cikti agaci gezilir, beyanla karsilastirilir
import { readdirSync, readFileSync, writeFileSync, statSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-envanter");
const envanter = JSON.parse(readFileSync(join(KOK, "envanter.json")));
const maliyet = JSON.parse(readFileSync(join(KOK, "maliyet.json")));   // ureticin okudugu
const SERVIS = ["toplayici", "dogrulayici", "faturalama", "is-emri"];

const dolas = (kok, alt = "") => readdirSync(join(kok, alt), { withFileTypes: true })
  .flatMap((g) => g.isDirectory() ? dolas(kok, join(alt, g.name)) : [join(alt, g.name)]);

// Denetci beyan dosyalarini degil, agacin kendisini okur.
let gezilenDosya = 0, gezilenBayt = 0;
const bulgu = [];
for (const s of SERVIS) {
  for (const yol of dolas(join(KOK, s))) {
    if (yol.endsWith(".json")) continue;                       // beyan dosyalari denetcinin girdisi degil
    const tam = join(KOK, s, yol);
    gezilenDosya++; gezilenBayt += statSync(tam).size;
    const bas = readFileSync(tam).subarray(0, 120).toString("latin1");
    const k = bas.match(/kopyalanmis: (\S+)@(\S+)/);
    if (k) bulgu.push({ servis: s, ad: k[1], surum: k[2], nasil: "kaynaga kopyalanmis" });
    const g = bas.match(/GOMULU:(\S+)@(\S+)/);
    if (g) bulgu.push({ servis: s, ad: g[1], surum: g[2], nasil: "ikiliye gomulu" });
  }
  const { inen } = JSON.parse(readFileSync(join(KOK, s, "cikti", "indirilenler.json")));
  for (const kayit of inen) {
    const [ad, surum] = kayit.split("@");
    bulgu.push({ servis: s, ad, surum, nasil: "kosum aninda inen" });
  }
}

// Beyan edilmis kosum bileseni: derleme araclari uretimde kosmaz, kosum kumesine girmez.
const beyanli = new Set(envanter.filter((k) => k.tur !== "derleme").map((k) => `${k.servis}|${k.ad}`));
const ucuncuTaraf = (ad) => ad.startsWith("bilesen-");
const beyansiz = bulgu.filter((b) => ucuncuTaraf(b.ad) && !beyanli.has(`${b.servis}|${b.ad}`));
const yanlisTutulan = bulgu.filter((b) => !ucuncuTaraf(b.ad));

const say = (nasil) => beyansiz.filter((b) => b.nasil === nasil).length;
const kosanBeyanli = envanter.filter((k) => k.tur !== "derleme").length;
const kosanToplam = kosanBeyanli + beyansiz.length;

console.log(`envanterin gordugu kosum bileseni ${kosanBeyanli}, ` +
  `derleme zamani ${envanter.length - kosanBeyanli}`);
console.log(`envanterin gormedigi ${beyansiz.length} -> ` +
  `kaynaga kopyalanmis ${say("kaynaga kopyalanmis")}, ` +
  `ikiliye gomulu ${say("ikiliye gomulu")}, kosum aninda inen ${say("kosum aninda inen")}`);
console.log(`kapsam %${(100 * kosanBeyanli / kosanToplam).toFixed(0)} ` +
  `(${kosanBeyanli}/${kosanToplam} kosan bilesen)`);

const SUTUN = [28, 22, 10, 16, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
console.log("");
satir(["kapi", "okudugu", "tuttugu", "yalniz kendisi", "yanlis tuttugu"]);
satir(["beyan dosyalarini okur", `${maliyet.dosya} dosya / ${maliyet.bayt} bayt`, 0, 0, 0]);
satir(["cikti agacini gezer", `${gezilenDosya} dosya / ${gezilenBayt} bayt`,
  beyansiz.length, beyansiz.length, yanlisTutulan.length]);

console.log(`yanlis tutulan ${yanlisTutulan.length} kayit: ` +
  `${yanlisTutulan.map((b) => b.servis).join(", ")} -> ${yanlisTutulan[0].ad}`);

// Gercek kosan kume sonraki bloga birakilir: beyanli + beyansiz.
writeFileSync(join(KOK, "gercek.json"), JSON.stringify([
  ...envanter.filter((k) => k.tur !== "derleme")
    .map(({ servis, ad, surum }) => ({ servis, ad, surum, gorunur: true })),
  ...beyansiz.map(({ servis, ad, surum }) => ({ servis, ad, surum, gorunur: false })),
]));
envanterin gordugu kosum bileseni 88, derleme zamani 19
envanterin gormedigi 14 -> kaynaga kopyalanmis 6, ikiliye gomulu 4, kosum aninda inen 4
kapsam %86 (88/102 kosan bilesen)

kapi                        okudugu               tuttugu   yalniz kendisi  yanlis tuttugu
beyan dosyalarini okur      12 dosya / 7124 bayt  0         0               0
cikti agacini gezer         17 dosya / 8685 bayt  14        14              3
yanlis tutulan 3 kayit: toplayici, dogrulayici, faturalama -> ic-kitaplik-olcum

Koşan 102 bileşenin 88’i envanterde görünür; 14’ü görünmez — 6’sı kaynağa kopyalanmış, 4’ü ikiliye gömülü, 4’ü koşum anında inen. Kapsam %86’dır. Eksik %14 ölçünün en pahalı parçasıdır, çünkü envanter bu bileşenleri “yok” diye değil, hiç göstermeyerek raporlar.

Tablo iki kapıyı yan yana koyar. Beyan dosyalarını okuyan kapı beyan dışı bileşenlerin sıfırını tutar; tutamaz, çünkü baktığı yer beyanın kendisidir. Kapı olarak marjinal değeri sıfırdır ve kapı olarak durması gerekmez: envanteri üretmesi yeterlidir, geçişe izin verme yetkisi ona hiçbir şey kazandırmaz. Kaldırılması daha iyi olan kapı budur.

Çıktı ağacını gezen kapı 14’ün 14’ünü tutar ve hepsini yalnız kendisi tutar — marjinal değeri tuttuğunun tamamıdır. Okuma bedeli küçüktür: 12 dosya ve 7.124 bayta karşı 17 dosya ve 8.685 bayt, farklı bir okuma, daha büyük bir okuma değil. Asıl bedel yanlış tutmadır: 17 bulgunun 3’ü kendi kodunun kopyasıdır. Her altı bulgudan biri boşuna bir incelemedir ve bu oranın ürettiği davranış bilinir — liste bir kez toptan “bilinen” diye işaretlenir, sonraki gerçek bulgu da aynı işaretin altında kalır.

Duyuru Geldiğinde

Envanterin değeri üretildiği anda değil, sorulduğu anda ölçülür. Soru hep aynı biçimdedir: şu bileşenin şu sürümünün altındakiler açık — bizde var mı, nerede?

DD24. Kusur duyurusu bir bileşen adı ve bir eşik sürümüdür; eşiğin altındaki sürümler açık sayılır. Duyuru çekilişinin tohumu 884301 görünürdür.

// olcum-agi/duyuru.mjs — kusur duyurusu geldiginde yanit: envanterli ve envantersiz yol
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-envanter");
const SERVIS = ["toplayici", "dogrulayici", "faturalama", "is-emri"];
const SURUM = ["1.4", "2.0", "2.3", "2.6", "3.1", "3.4"];
const oku = (ad) => JSON.parse(readFileSync(join(KOK, ad)));
const envanter = oku("envanter.json"), maliyet = oku("maliyet.json");
const gercek = oku("gercek.json");                 // onceki blogun denetimi: beyanli + beyansiz

const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(884301);                          // duyuru cekilisinin tohumu gorunur
const ADLAR = [...new Set(gercek.map((k) => k.ad))].sort();
const DUYURU = Array.from({ length: 9 }, () => ({
  ad: ADLAR[Math.floor(r() * ADLAR.length)],
  esik: SURUM[1 + Math.floor(r() * (SURUM.length - 1))],       // bu surumun altindakiler acik
}));
const etkilenen = (kume, d) => new Set(kume
  .filter((k) => k.ad === d.ad && SURUM.indexOf(k.surum) < SURUM.indexOf(d.esik))
  .map((k) => k.servis));

console.log(`gercek kosan kayit ${gercek.length} ` +
  `(gorunmeyen ${gercek.filter((k) => !k.gorunur).length}), duyuru ${DUYURU.length}; tohum 884301`);
console.log("");
console.log("duyuru".padEnd(24) + "envanter yaniti".padEnd(17) + "gercek".padEnd(9) + "durum");

let dogru = 0, eksik = 0, etkiliDuyuru = 0;
for (const d of DUYURU) {
  const yanit = etkilenen(gercek.filter((k) => k.gorunur), d), ger = etkilenen(gercek, d);
  if (ger.size) etkiliDuyuru++;
  yanit.size === ger.size ? dogru++ : eksik++;
  console.log(`${d.ad} < ${d.esik}`.padEnd(24) + `${yanit.size} servis`.padEnd(17) +
    `${ger.size} servis`.padEnd(9) + (yanit.size === ger.size ? "tam" : "eksik"));
}

const BEYAN = SERVIS.length * 3;                   // depo basina bildirim, cozum, derleme
console.log("");
console.log("yol".padEnd(30) + "duyuru basina".padEnd(26) + `${DUYURU.length} duyuru icin`);
console.log("envanter dosyasina sorgu".padEnd(30) + `1 sorgu / ${envanter.length} kayit`.padEnd(26) +
  `1 uretim (${maliyet.dosya} dosya) + ${DUYURU.length} sorgu`);
console.log("depolari tek tek acmak".padEnd(30) + `${SERVIS.length} depo / ${BEYAN} dosya`.padEnd(26) +
  `${DUYURU.length * SERVIS.length} depo acilisi, ${DUYURU.length * BEYAN} dosya`);
console.log("");
console.log(`etkilenen servisi olan duyuru ${etkiliDuyuru}/${DUYURU.length}; ` +
  `envanterin tam yanitladigi ${dogru}, eksik yanitladigi ${eksik}`);
gercek kosan kayit 102 (gorunmeyen 14), duyuru 9; tohum 884301

duyuru                  envanter yaniti  gercek   durum
bilesen-47 < 2.6        0 servis         1 servis eksik
bilesen-49 < 3.4        0 servis         1 servis eksik
bilesen-59 < 2.3        0 servis         0 servis tam
bilesen-41 < 2.0        0 servis         0 servis tam
bilesen-12 < 2.0        0 servis         0 servis tam
bilesen-12 < 2.3        0 servis         0 servis tam
bilesen-05 < 3.1        1 servis         1 servis tam
bilesen-58 < 2.3        0 servis         0 servis tam
bilesen-15 < 2.6        1 servis         2 servis eksik

yol                           duyuru basina             9 duyuru icin
envanter dosyasina sorgu      1 sorgu / 107 kayit       1 uretim (12 dosya) + 9 sorgu
depolari tek tek acmak        4 depo / 12 dosya         36 depo acilisi, 108 dosya

etkilenen servisi olan duyuru 4/9; envanterin tam yanitladigi 6, eksik yanitladigi 3

Dokuz duyurunun beşi hiçbir servisi etkilemez. En sık verilen yanıt “bizde yok”tur ve envanterin değeri öncelikle buradadır: o yanıt tek sorguda gelir. Envantersiz yol aynı yanıta ulaşmak için her duyuruda dört depoyu açar, on iki beyan dosyası okur — dokuz duyuru için 36 depo açılışı ve 108 dosya.

Bu, kapının eklediği sürenin yönünü belirler. Envanter üretimi hatta bir aşama ekler ve o aşama her değişiklikte 12 dosya okur; buna karşılık yanıt duyuru başına dört depo açılışından bir sorguya düşer. Kapının bedeli değişiklik başına, getirisi duyuru başınadır; oranı duyuru sıklığı belirler, kapının kendisi değil.

Son satır kapsamın bedelidir. Envanter 9 duyurunun 6’sını tam, 3’ünü eksik yanıtlar. İkisinde yanıt “0 servis”, gerçek “1 servis”tir; duyuru ilgisiz diye kapatılır. Üçüncüsü daha kötüdür: bilesen-15 için envanter “1 servis” der, gerçek “2 servis”tir. Sıfır bir yanıt bazen sorgulanır, bir sayı sorgulanmaz — iş yapılmış görünür. Görünmeyen 14 bileşenin bedeli budur ve çıktı ağacını gezen kapı kaldırıldığında geri gelir.

Özet

  • Dört servis 34 doğrudan bağımlılık bildirir; envanter 107 kayıt üretir — 34 doğrudan, 54 geçişli, 19 derleme zamanı aracı. Yalnız doğrudan bağımlılığa bakan bir kapı beyan edilen 88 koşum bileşeninin 34’ünü görür.
  • Üretim maliyeti ortamdan bağımsız yazılır: 12 beyan dosyası, 7.124 bayt okuma, 107 kayıt, 12.659 baytlık envanter, 56 benzersiz bileşen.
  • Koşan 102 bileşenin 88’i envanterde görünür, 14’ü görünmez: 6 kaynağa kopyalanmış, 4 ikiliye gömülü, 4 koşum anında inen. Kapsam %86’dır.
  • Beyan dosyalarını okuyan kapı beyan dışı bileşenin sıfırını tutar ve kapı olarak kaldırılabilir; çıktı ağacını gezen kapı 14’ünü tutar, hepsini yalnız kendisi tutar, bedeli 17 bulgunun 3’ünü yanlış tutmasıdır.
  • Yanıt duyuru başına 4 depo açılışından 1 sorguya iner: 9 duyuru için 36 depo açılışına karşı bir üretim ve 9 sorgu. Envanter 6 duyuruyu tam, 3’ünü eksik yanıtlar; eksiklerden biri “0” değil “1” der.

Sonraki Adım

Envanter artık var, kapsamı sayılı ve sorgulanabilir. Ama 12.659 baytlık bu dosya ile tarif ettiği çıktı arasında hiçbir bağ yoktur. Envanter ayrı bir dosyadır: yanına başka bir envanter konabilir, içindeki bir kayıt değiştirilebilir, bambaşka bir yapı çıktısı aynı envanterle eşleştirilebilir. 107 kaydın hiçbiri, tarif ettiği baytların gerçekten o baytlar olduğunu söylemez.

Aynı boşluk zincirin öteki halkalarında da vardır: kaynak işlemesi ile yapı çıktısı arasında, yapı çıktısı ile dağıtılan şey arasında. Bir sonraki ders bu halkaları gerçek bir anahtar çiftiyle bağlar ve iki soruyu sayıya çevirir: doğrulama zincirin hangi noktasında yapılırsa kaç sahte çıktı geçer, ve o anahtarı elinde tutan kaç özne vardı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