Ders 04 / 16
Gereksiz Veri Çekme
Aşırı veri çekmenin uçtan uca ölçülmesi: geniş kaydın önbelleğe sığan giriş sayısını düşürmesi, aynı isabet düşüşünü anahtar çeşitlenmesinin de vermesi, ikisini çalışma kümesinin iki çarpanına ayıran ölçüm ve fazla alan taşımanın haklı olduğu sınırın bir çağrının zarfıyla sınırlanması.
İçindekiler
Toplu çağrı taşınanı üçte bire indirdi, ama taşıdığı şeyin içine bakılmadı. Önceki dersin ölçüsü kayıt tanecikliğinde çalışıyordu: bir kaydın gelip gelmediğini sayıyordu, kaydın içindeki alanların okunup okunmadığını değil. İsraf alan tanecikliğinde de olur ve bu dersin konusu odur.
Katalogdaki başlık Gereksiz Veri Çekmedir; gövdede kullanılan terim aşırı veri çekme (over-fetching), Backend müfredatındaki Veri Erişim Katmanı kursuyla aynı biçimdir. Orada kavram kuruldu ve baytı ölçüldü: sütun seçimi, satır sınırlaması, yıldız seçiminin sessiz bedeli. Burada o mekanik tekrarlanmaz. Buradaki soru sistem tasarımı sorusudur: gereksiz bayt, deponun dışındaki katmanlarda neyi büyütür ve kesildiğinde ne sertleşir.
Belirti: İsabet Düşüyor, İstek Sayısı Sabit
Belirti kenar önbelleğinde görünüyor. K02 okumaların onda dokuzunu kenarda karşılıyordu; K01’in V9 varsayımı da 0,90 isabet öngörüyor ve buna göre depoya ulaşan okuma saniyede 41,67. Ölçülen isabet 0,90’ın altına indi, depoya ulaşan okuma büyüdü. Uçtaki istek sayısı değişmedi.
Neden A — kayıt genişledi. Aynı sayıda giriş, her biri daha çok bayt. Önbelleğe sığan giriş sayısı düştü, tahliye arttı.
Neden B — anahtar çeşitlendi. Kayıt aynı boyutta, ama aynı gönderi için birden çok anahtar üretiliyor: dil, biçim ya da tüketiciye göre ayrışan yanıtlar. Giriş sayısı aynı kaldı, çalışma kümesi büyüdü.
İkisi de aynı belirtiyi verir ve düzeltmeleri farklı yerdedir: biri yanıtın genişliğini, öteki yanıtın çeşidini ilgilendirir.
Ölçüm Düzeneği
Önbellek gerçek bir en az kullanılanı atan (LRU) düzendir ve bayt kapasitesiyle sınırlıdır. İstek izi tohumlu bir Zipf üretecinden geliyor; birkaç takip numarası çok, çoğu az sorgulanıyor.
KK4 — kenar önbelleğinin bu kayıt türüne ayırdığı bellek: 1 MiB (model ölçeğinde). Gerekçe: yirmi bin gönderilik evrenin tamamı sığmamalı, yoksa tahliye hiç olmaz ve genişliğin bedeli görünmez. Duyarlılığı iki ve dört katıyla son tabloda verildi.
// alan/uctan-uca.mjs — gereksiz baytin ucdan uca bedeli: onbellege sigan giris, isabet, // depoya ulasan istek ve ag cikisi. Onbellek gercek bir LRU, iz tohumlu bir Zipf uretecinden. const EVREN = 20_000, IZ = 200_000, KAPASITE = 1 << 20; // KK4: onbellek 1 MiB (model olcegi) const OLAY = 7; // K01 V4 const kayit = (i) => ({ no: `TR-2026-${String(i).padStart(9, "0")}`, satici: i % 20, gun: i % 30, tarife_id: i % 40, bolge_id: i % 12, agirlik: 1 + (i % 30), hacim: 10 + (i % 90), sozlesme: `SZ-${i % 20}`, oran: 12 + (i % 7) * 0.5, katsayi: 1 + (i % 5) * 0.1, indirim: (i % 9) * 0.01, olusma: 1700000000 + i * 37, guncelleme: 1700000000 + i * 41, durum: i % 5, tasiyici: `TSY-${i % 6}`, rota_kodu: `R-${i % 40}-${i % 12}`, teslim_penceresi: `${8 + (i % 4)}:00-${12 + (i % 4)}:00`, olaylar: Array.from({ length: OLAY }, (_, j) => ({ kod: (i * 3 + j) % 5, ts: 1700000000 + i * 41 + j * 900, bolge: (i + j) % 12, rota: `R-${(i + j) % 40}`, tasiyici: `TSY-${(i + j) % 6}`, sira: j, aciklama: `adim ${j} kayit alindi merkez ${(i + j) % 12}` })), }); const AD = ["kabul", "aktarma", "dagitimda", "teslim", "bekliyor"]; const BICIM = { tam: (r) => r, orta: (r) => ({ no: r.no, durum: r.durum, bolge_id: r.bolge_id, guncelleme: r.guncelleme, tasiyici: r.tasiyici, rota_kodu: r.rota_kodu, agirlik: r.agirlik, olaylar: r.olaylar.slice(-3).map((o) => ({ kod: o.kod, ts: o.ts, rota: o.rota, bolge: o.bolge, aciklama: o.aciklama })) }), ihtiyac: (r) => ({ no: r.no, durum: AD[r.durum], bolge: r.bolge_id, guncelleme: r.guncelleme, olaylar: r.olaylar.slice(-3).map((o) => ({ kod: o.kod, ts: o.ts, rota: o.rota })) }), }; const alanSay = (x) => Array.isArray(x) ? x.reduce((a, y) => a + alanSay(y), 0) : (x && typeof x === "object") ? Object.values(x).reduce((a, y) => a + alanSay(y), 0) : 1; function iz() { // tohumlu Zipf: populer takip numarasi let s = 20260801 % 2147483647; const rast = () => (s = (s * 48271) % 2147483647) / 2147483647; const w = Array.from({ length: EVREN }, (_, i) => 1 / (i + 1)); const kum = w.reduce((a, x, i) => (a.push((a[i - 1] ?? 0) + x), a), []); const top = kum[EVREN - 1]; const cek = () => { const h = rast() * top; let a = 0, b = EVREN - 1; while (a < b) { const m = (a + b) >> 1; if (kum[m] < h) a = m + 1; else b = m; } return a; }; return Array.from({ length: IZ }, cek); } const IZ_DIZI = iz(); function kosum(bicim, cesit, kapasite = KAPASITE) { // cesit: ayni kayit kac ayri anahtar uretiyor const onbellek = new Map(); // LRU: Map ekleme sirasini korur let dolu = 0, isabet = 0, tasinan = 0, seriAlan = 0; for (let t = 0; t < IZ_DIZI.length; t += 1) { const i = IZ_DIZI[t], anahtar = `${i}|${t % cesit}`; const govde = BICIM[bicim](kayit(i)); const b = Buffer.byteLength(JSON.stringify(govde)); tasinan += b; if (onbellek.has(anahtar)) { isabet += 1; onbellek.delete(anahtar); onbellek.set(anahtar, b); continue; } seriAlan += alanSay(govde); // isabetsizlikte seri hale getiriliyor while (dolu + b > kapasite && onbellek.size > 0) { const [ilk, ib] = onbellek.entries().next().value; onbellek.delete(ilk); dolu -= ib; } onbellek.set(anahtar, b); dolu += b; } const bayt = Buffer.byteLength(JSON.stringify(BICIM[bicim](kayit(1)))); return { bayt, alan: alanSay(BICIM[bicim](kayit(1))), cesit, giris: Math.floor(kapasite / bayt), isabet: isabet / IZ, seriAlan, tasinan }; } const OKUMA = 416.67, K01_ISABET = 0.9; // K01: tepe okuma istek/s, V9 const SENARYO = [["tam yanit", "tam", 1], ["orta yanit", "orta", 1], ["ihtiyac kadar", "ihtiyac", 1], ["ihtiyac, 4 cesitli anahtar", "ihtiyac", 4]]; const say = (x) => x.toLocaleString("tr-TR"); console.log(`model: ${say(EVREN)} gonderi, ${say(IZ)} istek (tohumlu Zipf, tohum 20260801), ` + `onbellek ${KAPASITE / 1024} KiB; K01: V5 takip yaniti 480 bayt, V7 gonderi kaydi 900 bayt`); console.log(`\n${"senaryo".padEnd(28)}${"kayit bayt".padStart(12)}${"alan".padStart(6)}` + `${"sigan giris".padStart(13)}${"anahtar cesidi".padStart(16)}${"isabet".padStart(9)}` + `${"depoya istek/s".padStart(16)}${"ag cikisi Mbit/s".padStart(18)}`); const r = {}; for (const [ad, bicim, cesit] of SENARYO) { const o = kosum(bicim, cesit); r[ad] = o; console.log(ad.padEnd(28) + say(o.bayt).padStart(12) + String(o.alan).padStart(6) + say(o.giris).padStart(13) + String(cesit).padStart(16) + o.isabet.toFixed(4).padStart(9) + (OKUMA * (1 - o.isabet)).toFixed(2).padStart(16) + ((OKUMA * o.bayt * 8) / 1e6).toFixed(3).padStart(18)); } console.log(`K01 varsayimi: isabet ${K01_ISABET} -> depoya ${(OKUMA * (1 - K01_ISABET)).toFixed(2)} istek/s`); const t = r["tam yanit"], i = r["ihtiyac kadar"], c = r["ihtiyac, 4 cesitli anahtar"]; console.log(`\nayirt eden olcum — calisma kumesi = anahtar cesidi x kayit bayt`); console.log(`${"senaryo".padEnd(28)}${"anahtar basina bayt".padStart(21)}${"cesit".padStart(7)}` + `${"kume bayt".padStart(12)}${"ihtiyaca gore".padStart(15)}`); for (const ad of ["tam yanit", "ihtiyac kadar", "ihtiyac, 4 cesitli anahtar"]) console.log(ad.padEnd(28) + say(r[ad].bayt).padStart(21) + String(r[ad].cesit).padStart(7) + say(r[ad].bayt * r[ad].cesit).padStart(12) + ((r[ad].bayt * r[ad].cesit) / i.bayt).toFixed(2).padStart(15)); console.log(`\nucdan uca fark (tam -> ihtiyac): kayit ${say(t.bayt)} -> ${say(i.bayt)} bayt ` + `(${(t.bayt / i.bayt).toFixed(2)} kat), sigan giris ${say(t.giris)} -> ${say(i.giris)}, ` + `isabet ${t.isabet.toFixed(4)} -> ${i.isabet.toFixed(4)}, depoya ` + `${(OKUMA * (1 - t.isabet)).toFixed(2)} -> ${(OKUMA * (1 - i.isabet)).toFixed(2)} istek/s`); console.log(`seri hale getirilen alan (isabetsizlikte): ${say(t.seriAlan)} -> ${say(i.seriAlan)} ` + `(${(t.seriAlan / i.seriAlan).toFixed(2)} kat); ag cikisi ` + `${((OKUMA * t.bayt * 8) / 1e6).toFixed(3)} -> ${((OKUMA * i.bayt * 8) / 1e6).toFixed(3)} Mbit/s`); console.log(`anahtar cesitlenmesinin bedeli (ihtiyac -> 4 cesit): isabet ${i.isabet.toFixed(4)} -> ` + `${c.isabet.toFixed(4)}, depoya ${(OKUMA * (1 - i.isabet)).toFixed(2)} -> ` + `${(OKUMA * (1 - c.isabet)).toFixed(2)} istek/s, ag cikisi degismedi`); const ZARF = 280; // 03. ders: bir cagrinin zarfi console.log(`\nfazla alan tasimanin hakli oldugu sinir: bir cagriyi ortadan kaldiriyorsa fazlalik ` + `${ZARF} bayttan (03. dersin zarfi) kucuk olmali; olculen fazlalik ` + `orta ${say(r["orta yanit"].bayt - i.bayt)} bayt, tam ${say(t.bayt - i.bayt)} bayt`); console.log(`\nKK4 duyarliligi (onbellek ${KAPASITE / 1024} KiB -> 2 ve 4 kati) isabet`); console.log(`${"senaryo".padEnd(28)}${"1 kat".padStart(9)}${"2 kat".padStart(9)}${"4 kat".padStart(9)}`); for (const [ad, bicim, cesit] of SENARYO) console.log(ad.padEnd(28) + [1, 2, 4] .map((k) => kosum(bicim, cesit, KAPASITE * k).isabet.toFixed(4).padStart(9)).join(""));
model: 20.000 gonderi, 200.000 istek (tohumlu Zipf, tohum 20260801), onbellek 1024 KiB; K01: V5 takip yaniti 480 bayt, V7 gonderi kaydi 900 bayt senaryo kayit bayt alan sigan giris anahtar cesidi isabet depoya istek/s ag cikisi Mbit/s tam yanit 1.124 66 932 1 0.6028 165.49 3.747 orta yanit 409 22 2.563 1 0.7226 115.58 1.363 ihtiyac kadar 207 13 5.065 1 0.8042 81.59 0.690 ihtiyac, 4 cesitli anahtar 207 13 5.065 4 0.6348 152.15 0.690 K01 varsayimi: isabet 0.9 -> depoya 41.67 istek/s ayirt eden olcum — calisma kumesi = anahtar cesidi x kayit bayt senaryo anahtar basina bayt cesit kume bayt ihtiyaca gore tam yanit 1.124 1 1.124 5.43 ihtiyac kadar 207 1 207 1.00 ihtiyac, 4 cesitli anahtar 207 4 828 4.00 ucdan uca fark (tam -> ihtiyac): kayit 1.124 -> 207 bayt (5.43 kat), sigan giris 932 -> 5.065, isabet 0.6028 -> 0.8042, depoya 165.49 -> 81.59 istek/s seri hale getirilen alan (isabetsizlikte): 5.242.710 -> 509.093 (10.30 kat); ag cikisi 3.747 -> 0.690 Mbit/s anahtar cesitlenmesinin bedeli (ihtiyac -> 4 cesit): isabet 0.8042 -> 0.6348, depoya 81.59 -> 152.15 istek/s, ag cikisi degismedi fazla alan tasimanin hakli oldugu sinir: bir cagriyi ortadan kaldiriyorsa fazlalik 280 bayttan (03. dersin zarfi) kucuk olmali; olculen fazlalik orta 202 bayt, tam 917 bayt KK4 duyarliligi (onbellek 1024 KiB -> 2 ve 4 kati) isabet senaryo 1 kat 2 kat 4 kat tam yanit 0.6028 0.6844 0.7684 orta yanit 0.7226 0.8056 0.8834 ihtiyac kadar 0.8042 0.8822 0.9157 ihtiyac, 4 cesitli anahtar 0.6348 0.7113 0.7788
Sayılar ölçüm sınıfındadır ve tohuma bağlıdır; K01 ölçeğine çevrilen istek/s ve Mbit/s sütunları hesaptır. Modelin ihtiyaç kadar yanıtı 207 bayt çıkıyor, K01’in V5 varsayımı 480 baytlıktı — varsayım daha tutucuydu ve taşınabilir olan mutlak bayt değil, senaryolar arası orandır.
Kayıt Genişlemesi ile Anahtar Çeşitlenmesi Aynı Belirtiyi Veriyor
İlk tablonun birinci ve dördüncü satırı bu dersin bütün meselesidir. Tam yanıtta isabet 0,6028 ve depoya ulaşan okuma saniyede 165,49. İhtiyaç kadar ama dört çeşit anahtar üreten yanıtta isabet 0,6348 ve depoya 152,15. İki senaryo aynı belirtiyi veriyor — isabet 0,90’ın çok altında, depoya ulaşan okuma K01’in öngördüğü 41,67’nin dört katı — ama biri geniş kayıttan, öteki çeşitlenmeden geliyor.
Ayırt eden ölçüm ikinci tablodadır ve tek bir çarpımdır: çalışma kümesi = anahtar çeşidi × anahtar başına bayt. İhtiyaç kadar yanıt taban alındığında tam yanıtın kümesi 5,43 kat, dört çeşitli yanıtın kümesi 4,00 kat. İki çarpandan hangisinin büyüdüğü nedeni söyler: tam yanıtta bayt (1.124’e karşı 207), dört çeşitli yanıtta çeşit (4’e karşı 1).
Üçüncü bir sütun tanıyı doğruluyor. Ağ çıkışı tam yanıtta 3,747 Mbit/s, dört çeşitli yanıtta 0,690 Mbit/s — değişmemiş. Aynı isabet düşüşü, çok farklı ağ çıkışı. Yalnız isabete bakan bir gösterge iki nedeni ayıramaz; kümenin iki çarpanı ayırır.
Gereksiz Baytın Dört Ayrı Yerdeki Bedeli
Tam yanıttan ihtiyaç kadar yanıta geçiş dört büyüklüğü birden oynatıyor ve dördü aynı oranda değişmiyor.
Kayıt 1.124 bayttan 207 bayta iniyor: 5,43 kat. Önbelleğe sığan giriş 932’den 5.065’e çıkıyor; oran yine 5,43 çünkü kapasite bayt cinsindendir. İsabet 0,6028’den 0,8042’ye çıkıyor — bu oran 5,43 değil, çünkü isabet giriş sayısına doğrusal bağlı değil, popülerlik dağılımına bağlı. Depoya ulaşan okuma 165,49’dan 81,59 istek/s’ye iniyor: 2,03 kat.
Seri hâle getirilen alan sayısı 5.242.710’dan 509.093’e iniyor: 10,30 kat. Bu, kayıt oranının (5,43) neredeyse iki katıdır ve nedeni birleşiktir: hem kayıt başına alan sayısı 66’dan 13’e inmiş, hem de isabetsizlik seyrelmiştir. Seri hâle getirme işi iki çarpanla birden azalıyor.
Ağ çıkışı 3,747 Mbit/s’den 0,690 Mbit/s’ye iniyor, tam olarak 5,43 kat. K01’in tepe okuma çıkışı 1,6 Mbit/s’ti; tam yanıt onu 2,34 katına çıkarır.
KK4 duyarlılık tablosu bir uyarı taşıyor: önbelleği dört katına çıkarmak tam yanıtın isabetini 0,6028’den 0,7684’e getiriyor, ama ihtiyaç kadar yanıtın 1 kattaki 0,8042’sine bile yetişemiyor. Bellek eklemek genişliği kapatmıyor.
Fazla Alan Taşımanın Haklı Olduğu Sınır
Fazla alan taşımak her zaman israf değildir; bir çağrıyı ortadan kaldırıyorsa hesabı vardır ve hesabın eşiği önceki dersten geliyor. Bir çağrının zarfı 280 bayttı. Öyleyse kural şudur: taşınan fazlalık, ortadan kaldırdığı çağrının zarfından küçükse haklıdır.
Ölçüm eşiği iki tarafa da uyguluyor. Orta yanıtın fazlalığı 202 bayt; 280’in altında, yani bu alanlar ikinci bir çağrıyı gereksizleştiriyorsa taşınmaları kârlıdır. Tam yanıtın fazlalığı 917 bayt; üç zarftan büyük, yani üç ayrı çağrıyı ortadan kaldırmadıkça savunulamaz.
Sınır ölçeğe bağlıdır ve ölçek değişince yön değişir. Zarf sıkıştırılmış bir protokolde küçülürse eşik düşer ve orta yanıt da savunulamaz hâle gelir; bir kaydın popülerliği çok yüksekse önbellekte zaten duruyordur ve fazlalığın isabet bedeli sıfıra yaklaşır. Karşıt kalıp bir yasak değil, bu iki sayının karşılaştırılmasıdır.
Karşılığında Büyüyen
Alanı daraltmanın karşılığı dördüncü satırda duruyor ve tuzak da orada. Yanıtı bir tüketicinin tam ihtiyacına göre kesmek, ikinci tüketiciyi karşılamaz; ikinci tüketici kendi alan kümesini ister ve ortaya ikinci bir çeşit çıkar. Dört tüketici dört çeşit demektir ve ölçüm bunun bedelini veriyor: isabet 0,8042’den 0,6348’e, depoya ulaşan okuma 81,59’dan 152,15 istek/s’ye.
Neden A’nın düzeltmesi Neden B’yi doğurur. Daraltma sonsuza kadar sürdürülemez; bir noktada tek bir orta yanıt, dört dar yanıttan ucuza gelir. Ölçüm hangisinin ucuz olduğunu söyler: orta yanıt tek çeşitle 0,7226 isabet ve 115,58 istek/s verirken, dar yanıt dört çeşitle 0,6348 ve 152,15 verir. Bu model ölçeğinde orta yanıt kazanıyor.
İkinci karşılık sözleşmededir ve sayılmaz: dar yanıt, tüketicinin bugünkü ihtiyacını yanıtın biçimine yazar. Yeni bir alan her istendiğinde ya yanıt genişler ya yeni bir çeşit doğar; ikisinin de bedeli yukarıdaki tablodadır.
Özet
- Belirti kenar önbelleğindedir: istek sayısı sabitken isabet K01’in V9 varsayımı olan 0,90’ın altına iniyor ve depoya ulaşan okuma 41,67 istek/s’nin katlarına çıkıyor.
- Aynı belirtiyi kayıt genişlemesi (isabet 0,6028) ve anahtar çeşitlenmesi (0,6348) verir; ayırt eden ölçüm çalışma kümesinin iki çarpanıdır — tam yanıtta bayt 5,43 kat, dört çeşitli yanıtta çeşit 4,00 kat. Ağ çıkışı doğrulamayı yapar: 3,747’ye karşı 0,690 Mbit/s.
- Gereksiz bayt dört yeri birden büyütür ve aynı oranda değil: kayıt 5,43 kat, sığan giriş 5,43 kat, depoya ulaşan okuma 2,03 kat, seri hâle getirilen alan 10,30 kat.
- Bellek eklemek genişliği kapatmaz: önbellek dört katına çıktığında tam yanıtın isabeti 0,7684 oluyor, ihtiyaç kadar yanıtın tek kattaki 0,8042’sine yetişmiyor.
- Fazlalık, ortadan kaldırdığı çağrının zarfından (280 bayt) küçükse haklıdır: orta yanıtın fazlalığı 202 bayt, tam yanıtınki 917 bayt.
- Daraltmanın karşılığı çeşitlenmedir: her tüketiciye ayrı dar yanıt vermek isabeti 0,8042’den 0,6348’e indiriyor ve bu model ölçeğinde tek bir orta yanıt (0,7226) daha ucuza geliyor.
Sonraki Adım
Buraya kadarki dört ölçüm de aynı yerde duruyordu: bir isteğin taşıdığı veri. Hesabın katmanı, istemcinin işi, çağrının taneciği ve alanın genişliği — hepsi taşınan şeyle ilgiliydi. Oysa bir isteğin maliyetinin bir parçası hiç taşınmaz. İstek, kendisine hizmet edecek nesneler kurulmadan başlayamaz: depo bağlantısı, seri hâle getirici, imza doğrulayıcı, biçim çözümleyici. Bu nesnelerin ne zaman kurulduğu bugüne kadar hiç sorulmadı; hepsinin hazır durduğu varsayıldı. Sonraki ders bu varsayımı ölçer: pahalı bir istemcinin her istekte yeniden yaratılması, nesne başına kurulum işinin tekrar sayısıyla çarpımı ve havuzun karşılığında büyüttüğü şey.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.