Ders 11 / 18
İçinden Yazma ve Arkadan Yazma
Yazma yolunun önbelleğe bağlanmasının iki düzeni: içinden yazmanın silmeyi kaldırıp isabeti 0,7285'ten 0,9170'e çıkarması, denormalizasyonun eklediği ikinci yazma hedefinin yolu iki depoya uzatması, arkadan yazmanın toplu aktarımla yazma başına depo yazmasını 2,00'dan 1,20'ye indirmesi ve bunun karşılığında bellekte bekleyen 200 olayın dayanıklılık penceresi olarak ölçülmesi.
İçindekiler
Önceki dersin bütün maliyeti tek bir kısıttan doğdu: önbellek yazmanın olduğu yerde değildi, yeni değeri koyamadı, yalnız eskisini sildi ve her silme bir depo okuması doğurdu. Gönderi durumunu depoya yazan yol uygulamanın kendi içinden geçtiğinde bu kısıt kalkar. Yazma iki yere birden gider ve silme hiç yapılmaz; ya da bir adım daha ileri gidilip yazma önce önbelleğe, depoya sonra toplu olarak gönderilir. Birincisine içinden yazma (write-through), ikincisine arkadan yazma (write-behind) denir. Bu ders iki düzeni ölçer.
İki stratejinin mekaniği Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda kuruldu ve ölçüldü; burada yeniden anlatılmaz. Buradaki soru ölçekleme sorusudur: yazma yolu kaç yere uzuyor, olay başına ne kadar ek iş çıkıyor ve K01’in hangi satırı oynuyor.
İkinci Yazma Hedefi
Bu konunun kurduğu bir sayı bu derste değişir. Önceki konu okuma yolunu kısaltmak için denormalizasyon uygulamıştı: bölge başına dağıtımdaki gönderi sayısı, sorgu anında sayılmak yerine ayrı bir sayaçta tutulur. O karar okuma tarafında kazanç sağladı, ama yazma tarafında sessiz bir borç bıraktı — her durum olayı artık iki yeri günceller: gönderinin takip kaydı ve gönderinin bulunduğu bölgenin sayacı.
K01’in tablosunda yalnız bir yazma vardı; tepe yazma istek/s 97,22, bir olayın bir kayda
yazıldığı varsayımıyla hesaplanmıştı. Yazma hedefi ikiye çıktığında olay hızı değişmez ama depo
yazma hızı değişir. Bu dersin ilk ölçüsü bu yüzden yazma yolunun uzunluğudur: bir olayın
kaç yere dokunduğu.
B1 (bu dersin varsayımı): taşıyıcı ağı 40 dağıtım bölgesine ayrılmıştır. Gerekçe: rota adımları bölge düzeyinde raporlanır ve V5’teki takip yanıtı bölgeyi bir alan olarak taşır. Duyarlılık son ölçümde yazılıdır: bölge sayısı büyüdükçe toplu aktarımdaki birleşme oranı düşer. Varsayım K01’in tablosuna eklenmez.
// onbellek/yazma.mjs — yazma yolunun onbellege baglandigi duzenlerin surec ici modeli. // Onbellek, depo ve olay akisi birer modul; sayaclar aciktir. Toplu pencere ve bolge // sayisi parametredir. Sureler olculmez, islem sayilir. export function kutuAc(kapasite) { const m = new Map(); const o = { tahliye: 0, var: (a) => m.has(a), al: (a) => { const d = m.get(a); if (d !== undefined) { m.delete(a); m.set(a, d); } return d; }, koy: (a, d) => { m.delete(a); m.set(a, d); if (m.size > kapasite) { m.delete(m.keys().next().value); o.tahliye += 1; } }, sil: (a) => m.delete(a), }; return o; } // Her durum olayi iki yeri gunceller: gonderinin takip kaydi ve bolgenin dagitim sayaci. export function akis({ okuma, oran, calismaKumesi, obek, bolge }) { let tohum = 20260730; const rast = () => ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648); let sonraki = 1; const etkin = Array.from({ length: calismaKumesi }, () => ({ no: sonraki++, kalan: obek })); const cikti = []; let borc = 0; for (let i = 0; i < okuma; i++) { const j = Math.floor(rast() * etkin.length); const g = etkin[j]; cikti.push(["oku", `takip:${g.no}`]); if ((g.kalan -= 1) === 0) etkin[j] = { no: sonraki++, kalan: obek }; for (borc += oran; borc >= 1; borc -= 1) { const h = etkin[Math.floor(rast() * etkin.length)].no; cikti.push(["yaz", `takip:${h}`, `bolge:${Math.floor(rast() * bolge) + 1}`]); } } return cikti; } // takipYazma: "sil" (yaninda okuma) ya da "guncelle" (icinden yazma). // bolgeYazma: "icinden" ya da "arkadan"; arkadan yazmada toplu pencere `topluPencere` yazmadir. export function kosum(islemler, { kapasite, takipYazma, bolgeYazma, topluPencere = 200 }) { const c = kutuAc(kapasite); const bolgeC = new Map(); const bekleyen = new Map(); // arkadan yazmada depoya gitmemis bolge sayaci const s = { isabet: 0, depoOkuma: 0, depoYazma: 0, dokunus: 0, yazmaDokunus: 0, bayat: 0, bekleyenOlay: 0 }; const dogru = new Map(); let sayim = 0, olayBorcu = 0, enCokBekleyenOlay = 0; for (const [tur, anahtar, bolge] of islemler) { if (!dogru.has(anahtar)) dogru.set(anahtar, 0); if (tur === "oku") { s.dokunus += 1; if (c.var(anahtar)) { s.isabet += 1; if (c.al(anahtar) !== dogru.get(anahtar)) s.bayat += 1; continue; } s.depoOkuma += 1; s.dokunus += 2; c.koy(anahtar, dogru.get(anahtar)); continue; } const deger = dogru.get(anahtar) + 1; dogru.set(anahtar, deger); s.depoYazma += 1; s.yazmaDokunus += 1; // takip kaydi her zaman depoya gider s.yazmaDokunus += 1; if (takipYazma === "sil") c.sil(anahtar); else c.koy(anahtar, deger); bolgeC.set(bolge, (bolgeC.get(bolge) ?? 0) + 1); // bolge sayaci once onbellekte s.yazmaDokunus += 1; if (bolgeYazma === "icinden") { s.depoYazma += 1; s.yazmaDokunus += 1; continue; } bekleyen.set(bolge, bolgeC.get(bolge)); olayBorcu += 1; enCokBekleyenOlay = Math.max(enCokBekleyenOlay, olayBorcu); if ((sayim += 1) % topluPencere === 0) { s.depoYazma += bekleyen.size; s.yazmaDokunus += bekleyen.size; bekleyen.clear(); olayBorcu = 0; } } s.bekleyenOlay = olayBorcu; return { ...s, bekleyenGirdi: bekleyen.size, enCokBekleyenOlay }; } // M19/K01 kabaca buyukluk hesabinin girdileri (V1-V4, V8). export const K01 = { tepeOkuma: (2_000_000 * 6 / 86_400) * 3, tepeYazma: (400_000 * 7 / 86_400) * 3, };
Model süreç içidir. Zaman ölçülmez, işlem sayılır; toplu aktarım bir süreyle değil bir yazma sayısıyla tetiklenir, çünkü süre bu makineye bağlı olurdu ve sayım değildir.
Üç Düzen
// onbellek/duzen.mjs — uc yazma duzeninin ayni akista karsilastirilmasi import { akis, kosum, K01 } from "./yazma.mjs"; const OKUMA = 200_000, KAPASITE = 2000, BOLGE = 40, PENCERE = 200; const ORAN = K01.tepeYazma / K01.tepeOkuma; const islemler = akis({ okuma: OKUMA, oran: ORAN, calismaKumesi: 1000, obek: 10, bolge: BOLGE }); const OLAY = islemler.filter(([t]) => t === "yaz").length; console.log(`okuma ${OKUMA}, durum olayi ${OLAY}, bolge ${BOLGE}, toplu pencere ${PENCERE} yazma`); console.log(`K01: tepe okuma ${K01.tepeOkuma.toFixed(2)}/s, tepe yazma ${K01.tepeYazma.toFixed(2)}/s\n`); const DUZEN = [ ["yaninda okuma + bolge icinden", { takipYazma: "sil", bolgeYazma: "icinden" }], ["icinden yazma + bolge icinden", { takipYazma: "guncelle", bolgeYazma: "icinden" }], ["icinden yazma + bolge arkadan", { takipYazma: "guncelle", bolgeYazma: "arkadan" }], ]; console.log("duzen isabet depo okuma depo yazma yazma/olay yol uzunlugu depoya ulasan/s depoda yazma/okuma bekleyen olay"); console.log("------------------------------ ------ ----------- ----------- ----------- ------------- ---------------- ------------------- -------------"); for (const [ad, secim] of DUZEN) { const r = kosum(islemler, { kapasite: KAPASITE, topluPencere: PENCERE, ...secim }); const h = r.isabet / OKUMA; const arka = K01.tepeOkuma * (1 - h); const yazmaS = K01.tepeYazma * (r.depoYazma / OLAY); console.log(`${ad.padEnd(30)} ${h.toFixed(4).padStart(6)} ${String(r.depoOkuma).padStart(11)} ` + `${String(r.depoYazma).padStart(11)} ${(r.depoYazma / OLAY).toFixed(2).padStart(11)} ` + `${(r.yazmaDokunus / OLAY).toFixed(2).padStart(13)} ` + `${(arka + yazmaS).toFixed(2).padStart(16)} ${(yazmaS / arka).toFixed(2).padStart(19)} ` + `${String(r.enCokBekleyenOlay).padStart(13)}`); } const arkadan = kosum(islemler, { kapasite: KAPASITE, topluPencere: PENCERE, takipYazma: "guncelle", bolgeYazma: "arkadan" }); console.log(`\nayni kural takip kaydina da uygulansaydi kayip penceresi:`); console.log(` bellekte bekleyen olay = ${arkadan.enCokBekleyenOlay}`); console.log(` olay kaydi 220 bayt (V6) -> ${arkadan.enCokBekleyenOlay * 220} bayt yazilmamis`); console.log(` tepe yazma ${K01.tepeYazma.toFixed(2)}/s -> ${(arkadan.enCokBekleyenOlay / K01.tepeYazma).toFixed(2)} saniyelik olay akisi`); console.log("\ntoplu pencerede ayni anahtara dusen yazma sayisi birlesme oranidir:"); console.log("bolge sayisi depo yazma yazma/olay depoya ulasan/s"); for (const bolge of [40, 200, 1000]) { const a = akis({ okuma: OKUMA, oran: ORAN, calismaKumesi: 1000, obek: 10, bolge }); const r = kosum(a, { kapasite: KAPASITE, topluPencere: PENCERE, takipYazma: "guncelle", bolgeYazma: "arkadan" }); const arka = K01.tepeOkuma * (1 - r.isabet / OKUMA); const yazmaS = K01.tepeYazma * (r.depoYazma / OLAY); console.log(`${String(bolge).padStart(12)} ${String(r.depoYazma).padStart(11)} ` + `${(r.depoYazma / OLAY).toFixed(2).padStart(11)} ${(arka + yazmaS).toFixed(2).padStart(16)}`); }
okuma 200000, durum olayi 46666, bolge 40, toplu pencere 200 yazma
K01: tepe okuma 416.67/s, tepe yazma 97.22/s
duzen isabet depo okuma depo yazma yazma/olay yol uzunlugu depoya ulasan/s depoda yazma/okuma bekleyen olay
------------------------------ ------ ----------- ----------- ----------- ------------- ---------------- ------------------- -------------
yaninda okuma + bolge icinden 0.7285 54308 93332 2.00 4.00 307.59 1.72 0
icinden yazma + bolge icinden 0.9170 16592 93332 2.00 4.00 229.01 5.63 0
icinden yazma + bolge arkadan 0.9170 16592 55908 1.20 3.20 151.04 3.37 200
ayni kural takip kaydina da uygulansaydi kayip penceresi:
bellekte bekleyen olay = 200
olay kaydi 220 bayt (V6) -> 44000 bayt yazilmamis
tepe yazma 97.22/s -> 2.06 saniyelik olay akisi
toplu pencerede ayni anahtara dusen yazma sayisi birlesme oranidir:
bolge sayisi depo yazma yazma/olay depoya ulasan/s
40 55908 1.20 151.04
200 75983 1.63 192.87
1000 88862 1.90 219.70
Bütün sayılar hesap sınıfındadır: belirlenimli bir akış üzerinde sayılmışlardır ve süre ölçülmemiştir.
Sayıların Okunması
İçinden yazma okuma tarafını düzeltiyor. Silme kalkınca isabet 0,7285’ten 0,9170’e çıktı ve depo okuması 54.308’den 16.592’ye indi. Dikkat çeken şey, 0,9170’in yazmasız koşumun 0,8976’sının da üstünde olmasıdır: yazma yalnız girdiyi düşürmemekle kalmıyor, onu ısıtıyor da. Bir gönderi olay ürettiği anda önbelleğe giriyor ve sonraki ilk sorgu ıska almıyor. V9’un 0,90’ı burada ilk kez aşılıyor ve nedeni bir varsayım değil, yazma yolunun bağlanmasıdır.
Yazma yolu iki depoya uzamış durumda. İlk iki düzende olay başına depo yazması 2,00; yol
uzunluğu 4,00 dokunuş (iki depo yazması, bir takip önbelleği güncellemesi, bir bölge sayacı
güncellemesi). Bu, K01’in görmediği bir maliyettir: tablo bir olayın bir kayda yazıldığını
varsayıyordu. İkinci yazma hedefi denormalizasyon kararının bedelidir ve depoya ulasan istek/s
satırını yukarı çeker.
Arkadan yazma yazma tarafını düzeltiyor. Bölge sayacı önce önbellekte tutulup her 200
yazmada bir toplu aktarıldığında olay başına depo yazması 2,00’dan 1,20‘ye iniyor. Aynı
bölgeye düşen ardışık artışlar tek bir güncellemede birleşiyor. depoya ulasan istek/s 229,01’den
151,04‘e düşüyor ve depoda yazma/okuma orani 5,63’ten 3,37’ye iniyor.
Üç düzenin depoya ulasan istek/s sütunu birlikte okunmalıdır: 307,59 → 229,01 → 151,04. K01’in
hesabındaki 138,89 bunların hiçbirine eşit değil, çünkü o hesap ne geçersizleştirmeyi ne ikinci
yazma hedefini biliyordu. 151,04, tasarım bittikten sonra o satırın gerçek değeridir.
Birleşme oranı anahtar uzayına bağlı. Son tablo kuralı veriyor: toplu pencerede aynı anahtara
kaç yazma düşerse o kadar kazanılır. Kırk bölgede 200 yazmalık pencere olay başına 1,20 depo
yazması bırakıyor; bölge sayısı 1000’e çıktığında pencere içinde neredeyse her yazma ayrı bir
anahtara düşüyor ve oran 1,90’a, depoya ulasan istek/s 219,70’e yükseliyor. Arkadan yazma
küçük ve sıcak bir anahtar uzayında kazandırır; geniş bir uzayda yalnız gecikme ekler.
Hangi Veri Hangi Düzene
Arkadan yazmanın bedeli son üç satırda yazılı. Toplu pencere 200 yazma olduğuna göre bellekte her an 200 olaya kadar yazılmamış değişiklik bulunur; tepe yazma hızında bu 2,06 saniyelik olay akışıdır. Süreç o anda dursa bu değişiklikler kaybolur.
Bölge sayacı için kayıp katlanılabilir, çünkü sayaç türetilmiş bir değerdir: olay kayıtları depodadır ve sayaç onlardan yeniden hesaplanabilir. Takip kaydı ve olay kaydının kendisi için katlanılamaz. V6’ya göre bir olay kaydı 220 bayttır ve V12’ye göre 730 gün saklanır; saklama süresinin gerekçesi sözleşme uyuşmazlıklarıdır. Kaybolan 200 olay 44.000 bayttır ve o baytların hiçbir yerde ikinci bir kopyası yoktur.
Kural bu yüzden veriye göre yazılır, katmana göre değil: kaynağı başka bir yerde duran veri arkadan yazmayla yazılabilir, kendisi kaynak olan veri yazılamaz. Aynı önbellek katmanında iki anahtar iki ayrı düzenle çalışır ve bu bir tutarsızlık değil, bir yerleşim kararıdır.
Özet
- Denormalizasyonun eklediği bölge sayacı yazma yolunu iki depoya uzattı: olay başına depo yazması 2,00, yol uzunluğu 4,00 dokunuş. K01’in tablosu bir olayın tek bir kayda yazıldığını varsayıyordu.
- İçinden yazma silmeyi kaldırınca isabet 0,7285’ten 0,9170’e çıktı — V9’un 0,90’ının üstüne — çünkü yazma girdiyi düşürmek yerine ısıtıyor; depo okuması 54.308’den 16.592’ye indi.
- Arkadan yazma bölge sayacında olay başına depo yazmasını 1,20’ye indirdi;
depoya ulasan istek/s229,01’den 151,04’e,depoda yazma/okuma orani5,63’ten 3,37’ye indi. - Birleşme oranı toplu pencerede aynı anahtara düşen yazma sayısıdır: 40 bölgede olay başına
1,20 depo yazması, 1000 bölgede 1,90 ve
depoya ulasan istek/s219,70. - Arkadan yazmanın kayıp penceresi 200 olay, 44.000 bayt ve 2,06 saniyelik akıştır; kaynağı başka yerde duran türetilmiş veride kabul edilir, kendisi kaynak olan olay kaydında edilmez.
Sonraki Adım
Bu derste yazma, önbellekteki girdiyi ayakta tuttu; ama yazma gelmeyen bir anahtarın girdisi yalnız bir nedenle düşer: süresi dolar. Süre dolduğu anda o anahtarı soran istek ıska alır, depoya gider ve bekler — üstelik anahtar sıcaksa aynı anda birçok istek aynı şeyi yapar. Süre dolmasını beklemek zorunlu değildir: girdi hâlâ geçerliyken, ömrünün sonuna yaklaştığında arka planda yenilenebilir ve okuyucu hiç ıska görmez. Bu yenileme bedava değildir; yenilenen girdilerin bir kısmı bir daha hiç okunmaz ve o tazelemeler doğrudan depoya yazılmış boş yüktür. Sonraki ders eşiği bir parametre olarak oynatır ve iki sayıyı karşı karşıya koyar: kurtarılan ıska ile boşa giden tazeleme.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.