İçeriğe geç
academia.sh

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/s 229,01’den 151,04’e, depoda yazma/okuma orani 5,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/s 219,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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat