İçeriğe geç
academia.sh

Ders 10 / 18

Yanında Okuma

Uygulama denetimli önbelleğin ölçekleme aracı olarak ele alınması: isabet oranının yazma/okuma oranının bir işlevi hâline getirilmesi, giriş kursunun 0,2333'lük oranında 0,90 varsayımının 0,7287'ye düşmesi, her geçersizleştirmenin bir depo okuması doğurması ve önbellek katmanı kısmen bozulduğunda uygulamanın depoya kendi gidebilmesinin ölçülmesi.

İçindekiler

Önceki ders dört katmanı bir zincir olarak ölçtü ve hepsi aynı biçimde davrandı: bak, yoksa bir alttakine sor, dönüşte koy. O davranışın adı yanında okuma (cache-aside) ve önbellek katmanının isabeti yalnız kapasiteye bağlı değil; girdiyi düşüren ikinci bir kuvvet var ve o kuvvet yazmadır. Kabaca büyüklük hesabı deponun gördüğü yerde yazma ağırlıklı bir sistem tarif ediyordu: depoda yazma/okuma orani 2,33. Bu ders isabet oranını sabit bir sayı olmaktan çıkarıp yazma sıklığının bir işlevi hâline getirir.

Yanında okumanın mekaniği Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda kuruldu ve ölçüldü; burada yeniden anlatılmaz. Buradaki iki soru başkadır: bu strateji hangi katmana ait ve K01’in yazma hızında ne veriyor.

Uygulama Denetimli Olmak Ne Demek

Yanında okumanın tanımlayıcı özelliği, önbelleğin okuma yolunun yanında durmasıdır: veriyi getirme sorumluluğu önbellekte değil, uygulamadadır. Aynı okuma yolunun iki gerçekleştirimi vardır — uygulama kodunun elle baktığı biçim ve okumanın önbellek katmanının üzerinden geçtiği biçim — ve ikisi de bu adla anılır. Fark mekanikte değil, sorumluluğun nerede durduğundadır ve son bölümde ölçülen tek şey odur.

Bu strateji, önbelleğin yazma yoluna bağlanamadığı her yerde tek seçenektir. Gönderi takip servisinde durum olayları uygulama katmanının okuma yolundan gelmez: taşıyıcıdan gelir, Uygulama Katmanı ve Servis Etkileşimi kursunda tasarlanan kuyruk üzerinden işlenir ve depoya oradan yazılır. Okuma yolundaki önbellek o yazmanın olduğu yerde değildir; yazıldığını ancak bir geçersizleştirme iletisiyle öğrenir. Bu yüzden okuma yolu önbelleğinin yapabileceği tek şey girdiyi silmektir, güncellemek değil. Yazmanın önbelleğe bağlanabildiği durum sonraki dersin konusudur.

Katman seçimi de buradan çıkar. Önceki dersin ölçümünde uygulama katmanındaki paylaşılan önbellek isabetin yalnız küçük bir payını durduruyordu, ama geri alınabilirliği en yüksek katman oydu: bir silme iletisi ancak orada anında uygulanabilir. Yanında okuma bu yüzden uygulama katmanına konur; kenar ve istemci katmanları aynı stratejiyi çalıştıramaz çünkü onlarda silme yoktur, yalnız süre vardır.

Yazmanın İsabete Etkisi

Model süreç içidir ve tek katmanlıdır: önceki dersin ölçümü kapasitesi 2000 olan tek bir paylaşılan önbelleğin çalışma kümesini karşıladığını gösterdiği için burada kapasite bağlayıcı kısıt olmaktan çıkarılır ve tek değişken yazma oranı olur.

B1 (bu dersin varsayımı): durum olayları, o anda sorgulanan gönderilere düşer. Gerekçe: bir gönderi tam da yolda olduğu için hem olay üretir hem sorgulanır. Bu, geçersizleştirmenin en güçlü olduğu yöndür; duyarlılık: olayların yarısı sorgulanmayan gönderilere düşerse silme sayısı ve doğurduğu ek okuma yarıya iner. Varsayım K01’in tablosuna eklenmez.

// onbellek/yaninda.mjs — yaninda okumanin surec ici modeli. Onbellek, depo ve olay akisi
// birer modul; sayaclar aciktir. Yazma/okuma orani, kapasite ve ariza payi parametredir.
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;
}

// Akis: okuma ve yazma ayni etkin gonderi kumesine duser (B1). `oran` yazma/okuma oranidir.
export function akis({ okuma, oran, calismaKumesi, obek }) {
  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)
      cikti.push(["yaz", `takip:${etkin[Math.floor(rast() * etkin.length)].no}`]);
  }
  return cikti;
}

// Yaninda okuma: okumada bak-getir-koy, yazmada depoya yaz ve girdiyi sil.
// gecersizlestir=false: yazma girdiyi silmez. ariza: onbellek isleminin basarisiz olma payi.
// atlayabilir=false: okuma onbellegin uzerinden gecer, uygulama depoya kendi gidemez.
export function kosum(islemler, { kapasite, gecersizlestir = true, ariza = 0, atlayabilir = true }) {
  const c = kutuAc(kapasite);
  let tohum = 991;
  const bozuk = () => ariza > 0 && ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648) < ariza;
  const s = { isabet: 0, iska: 0, depoOkuma: 0, depoYazma: 0, silme: 0, bayat: 0, dokunus: 0, karsilanmayan: 0 };
  const dogru = new Map();
  let surum = 0;
  for (const [tur, anahtar] of islemler) {
    if (!dogru.has(anahtar)) dogru.set(anahtar, 0);
    if (tur === "oku") {
      s.dokunus += 1;
      if (bozuk()) {
        if (!atlayabilir) { s.karsilanmayan += 1; continue; }
        s.iska += 1; s.depoOkuma += 1; s.dokunus += 1;
        continue;
      }
      if (c.var(anahtar)) {
        s.isabet += 1;
        if (c.al(anahtar) !== dogru.get(anahtar)) s.bayat += 1;
        continue;
      }
      s.iska += 1; s.depoOkuma += 1; s.dokunus += 2;
      c.koy(anahtar, dogru.get(anahtar));
    } else {
      dogru.set(anahtar, (surum += 1));
      s.depoYazma += 1; s.dokunus += 1;
      if (!gecersizlestir) continue;
      s.dokunus += 1;
      if (bozuk()) continue;                       // silme kaybolur: girdi bayat kalir
      if (c.sil(anahtar)) s.silme += 1;
    }
  }
  return { ...s, tahliye: c.tahliye };
}

// 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,
};
// onbellek/oran.mjs — isabet oraninin yazma/okuma oraninin islevi olarak olculmesi
import { akis, kosum, K01 } from "./yaninda.mjs";

const OKUMA = 200_000, KAPASITE = 2000, CALISMA = 1000, OBEK = 10;
const K01_ORAN = K01.tepeYazma / K01.tepeOkuma;

console.log(`okuma ${OKUMA}, kapasite ${KAPASITE}, es zamanli gonderi ${CALISMA}, obek ${OBEK}`);
console.log(`K01 yazma/okuma orani = ${K01_ORAN.toFixed(4)} (tepe yazma ${K01.tepeYazma.toFixed(2)} / tepe okuma ${K01.tepeOkuma.toFixed(2)})`);
console.log();
console.log("yazma/okuma   yazma   isabet  depo okuma  onbellek arkasi/s  depoya ulasan/s  depoda yazma/okuma");
console.log("----------- ------- -------- ----------- ------------------ ---------------- ------------------");
for (const oran of [0, 0.05, 0.1167, K01_ORAN, 0.4667, 1]) {
  const islemler = akis({ okuma: OKUMA, oran, calismaKumesi: CALISMA, obek: OBEK });
  const r = kosum(islemler, { kapasite: KAPASITE });
  const h = r.isabet / OKUMA;
  const arka = K01.tepeOkuma * (1 - h);
  const etiket = Math.abs(oran - K01_ORAN) < 1e-9 ? `${oran.toFixed(4)}*` : oran.toFixed(4);
  console.log(`${etiket.padStart(11)} ${String(r.depoYazma).padStart(7)} ${h.toFixed(4).padStart(8)} ` +
    `${String(r.depoOkuma).padStart(11)} ${arka.toFixed(2).padStart(18)} ` +
    `${(arka + K01.tepeYazma).toFixed(2).padStart(16)} ${(K01.tepeYazma / arka).toFixed(2).padStart(18)}`);
}
console.log("* K01'in tepe hizlarindan cikan oran");

console.log();
const islemler = akis({ okuma: OKUMA, oran: K01_ORAN, calismaKumesi: CALISMA, obek: OBEK });
const ile = kosum(islemler, { kapasite: KAPASITE });
const olmadan = kosum(islemler, { kapasite: KAPASITE, gecersizlestir: false });
console.log(`K01 oraninda gecersizlestirme ile   : isabet ${(ile.isabet / OKUMA).toFixed(4)}  depo okuma ${ile.depoOkuma}  silme ${ile.silme}  bayat okuma ${ile.bayat}`);
console.log(`K01 oraninda gecersizlestirme olmadan: isabet ${(olmadan.isabet / OKUMA).toFixed(4)}  depo okuma ${olmadan.depoOkuma}  silme ${olmadan.silme}  bayat okuma ${olmadan.bayat}`);
console.log(`silme basina dogan ek depo okumasi  = ${((ile.depoOkuma - olmadan.depoOkuma) / ile.silme).toFixed(2)}`);
console.log(`yazma basina ek islem               = ${((ile.dokunus - olmadan.dokunus) / ile.depoYazma).toFixed(2)} dokunus + ${((ile.depoOkuma - olmadan.depoOkuma) / ile.depoYazma).toFixed(2)} depo okumasi`);
okuma 200000, kapasite 2000, es zamanli gonderi 1000, obek 10
K01 yazma/okuma orani = 0.2333 (tepe yazma 97.22 / tepe okuma 416.67)

yazma/okuma   yazma   isabet  depo okuma  onbellek arkasi/s  depoya ulasan/s  depoda yazma/okuma
----------- ------- -------- ----------- ------------------ ---------------- ------------------
     0.0000       0   0.8976       20483              42.67           139.90               2.28
     0.0500   10000   0.8551       28976              60.37           157.59               1.61
     0.1167   23340   0.8039       39212              81.69           178.91               1.19
    0.2333*   46666   0.7287       54257             113.04           210.26               0.86
     0.4667   93340   0.6134       77312             161.07           258.29               0.60
     1.0000  200000   0.4523      109549             228.23           325.45               0.43
* K01'in tepe hizlarindan cikan oran

K01 oraninda gecersizlestirme ile   : isabet 0.7287  depo okuma 54257  silme 33957  bayat okuma 0
K01 oraninda gecersizlestirme olmadan: isabet 0.8977  depo okuma 20451  silme 0  bayat okuma 105794
silme basina dogan ek depo okumasi  = 1.00
yazma basina ek islem               = 2.45 dokunus + 0.72 depo okumasi

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.

Birinci satır yazma yokken önceki dersin sonucunu tekrarlıyor: isabet 0,8976, depoya ulasan istek/s 139,90. K01’in varsayımı burada tutuyor. K01’in kendi yazma hızında ise tutmuyor. Yazma/okuma oranı 0,2333 olduğunda isabet 0,7287‘ye düşüyor, onbellek arkasi okuma/s 41,67’den 113,04’e, depoya ulasan istek/s 138,89’dan 210,26’ya çıkıyor. V9 = 0,90 varsayımı örtük olarak “önbelleğe hiç yazılmıyor” demekti; sistem kendi yazma hızını gördüğünde varsayım yüzde on dokuz eksiliyor.

depoda yazma/okuma orani sütunu daha da ilginç. K01 bu oranı 2,33 hesaplamış ve “okuma ağırlıklı diye tarif edilen sistem, deponun gördüğü yerde yazma ağırlıklıdır” demişti. Yanında okumanın gerçek isabetiyle oran 0,86’ya iniyor: geçersizleştirme deponun okuma yükünü öyle büyütüyor ki depo yeniden okuma ağırlıklı hâle geliyor. Yazma sayısı hiç değişmedi; değişen, her yazmanın doğurduğu okumadır.

Son üç satır o doğurmayı sayıyor. K01 oranında 33.957 girdi silindi ve depo okuması 20.451’den 54.257’ye çıktı: silme başına 1,00 ek depo okuması. Silinen girdi neredeyse her zaman yeniden okunuyor, çünkü silinme nedeni tam da o gönderinin etkin olmasıdır. Bir yazmanın önbelleğe maliyeti böylece “bir silme” değil, “bir silme artı 0,72 depo okuması”dır.

Geçersizleştirme kapatıldığında isabet 0,8977’ye geri dönüyor ve fatura başka bir sütunda görünüyor: 105.794 bayat okuma, yani okumaların yarıdan fazlası. Yanında okumanın ödünleşimi bu iki sütun arasındadır ve ikisinin ortasındaki bölge — kabul edilebilir bayatlık penceresi — bu konunun son dersinin konusudur.

Uygulamanın Depoya Kendi Gidebilmesi

İki gerçekleştirim biçimi arasındaki fark, önbellek katmanı bozulduğunda ölçülebilir hâle gelir. Model, önbellek işlemlerinin bir payını başarısız kılar; uygulama denetimli biçim bunu ıska sayıp depoya gider, üzerinden geçen biçim ise isteği karşılayamaz.

// onbellek/ariza.mjs — onbellek katmani kismen bozuldugunda iki bicimin davranisi
import { akis, kosum, K01 } from "./yaninda.mjs";

const OKUMA = 200_000, KAPASITE = 2000;
const ORAN = K01.tepeYazma / K01.tepeOkuma;
const islemler = akis({ okuma: OKUMA, oran: ORAN, calismaKumesi: 1000, obek: 10 });

console.log("ariza  bicim               karsilanan  isabet  depo okuma  depoya ulasan/s  bayat okuma");
console.log("------ ------------------- ---------- ------- ----------- ---------------- -----------");
for (const ariza of [0, 0.1, 0.5, 1]) {
  for (const [ad, atlayabilir] of [["uygulama denetimli", true], ["uzerinden gecen", false]]) {
    const r = kosum(islemler, { kapasite: KAPASITE, ariza, atlayabilir });
    const karsilanan = OKUMA - r.karsilanmayan;
    const h = r.isabet / OKUMA;
    const depoS = K01.tepeOkuma * (r.depoOkuma / OKUMA) + K01.tepeYazma;
    console.log(`${ariza.toFixed(2).padStart(6)} ${ad.padEnd(19)} ${(karsilanan / OKUMA).toFixed(4).padStart(10)} ` +
      `${h.toFixed(4).padStart(7)} ${String(r.depoOkuma).padStart(11)} ${depoS.toFixed(2).padStart(16)} ${String(r.bayat).padStart(11)}`);
  }
}
ariza  bicim               karsilanan  isabet  depo okuma  depoya ulasan/s  bayat okuma
------ ------------------- ---------- ------- ----------- ---------------- -----------
  0.00 uygulama denetimli      1.0000  0.7287       54257           210.26           0
  0.00 uzerinden gecen         1.0000  0.7287       54257           210.26           0
  0.10 uygulama denetimli      1.0000  0.6468       70650           244.41        7473
  0.10 uzerinden gecen         0.8974  0.6468       50126           201.65        7473
  0.50 uygulama denetimli      1.0000  0.3209      135818           380.18       18780
  0.50 uzerinden gecen         0.4876  0.3209       33334           166.67       18780
  1.00 uygulama denetimli      1.0000  0.0000      200000           513.89           0
  1.00 uzerinden gecen         0.0000  0.0000           0            97.22           0

Sağlıklı durumda iki biçim aynı satırı üretiyor; fark yalnız arıza altında var. Önbellek tamamen çöktüğünde uygulama denetimli biçim isteklerin tamamını karşılamayı sürdürüyor ve depoya ulaşan istek 513,89 istek/s’ye çıkıyor — K01’in uçtaki tepe yükünün tamamı. Bu sayı bir tasarım şartıdır: yanında okumayı seçmek, deponun bu yükü kısa süre taşıyabilmesini şart koşar. Üzerinden geçen biçimde ise karşılanan istek oranı sıfıra iniyor; servisin hizmet erişilebilirliği önbellek katmanının ayakta kalmasına bağlanmış olur.

Depoya ulaşan istek sütunu bu satırlarda yanıltıcıdır ve bilerek yan yana konmuştur: yüzde elli arızada üzerinden geçen biçimin deposu 166,67 istek/s ile sağlıklı durumdakinden daha az yük görüyor. Depo yükü düşük görünürken isteklerin yarısı hiç yanıtlanmıyor. Bir ölçütün iyileşmesi tek başına okunamaz.

Üçüncü sütun beklenmedik bir sonucu taşıyor: yüzde on arızada 7473 bayat okuma var, oysa sağlıklı koşumda hiç yoktu. Kaybolan şey okumalar değil silmelerdir; başarısız bir silme, hiçbir sayaçta hata olarak görünmeden bayat bir girdi bırakır. Yanında okumanın doğruluğu, en gürültüsüz biçimde başarısız olan işleme bağlıdır.

Özet

  • Yanında okuma, veriyi getirme sorumluluğunun önbellekte değil uygulamada olduğu okuma yolu önbelleğidir; yazmanın önbelleğe bağlanamadığı yerde tek seçenektir ve yapabildiği tek şey girdiyi silmektir.
  • İsabet oranı yazma/okuma oranının işlevidir: yazma yokken 0,8976, K01’in 0,2333’lük oranında 0,7287. V9 = 0,90 varsayımı örtük olarak yazmasız bir sistem varsayıyordu.
  • K01’e dönüş: onbellek arkasi okuma/s 41,67’den 113,04’e, depoya ulasan istek/s 138,89’dan 210,26’ya çıkıyor; depoda yazma/okuma orani 2,33’ten 0,86’ya iniyor, yani depo yeniden okuma ağırlıklı hâle geliyor.
  • Silme başına 1,00 ek depo okuması doğuyor; bir yazmanın önbelleğe maliyeti 2,45 dokunuş ve 0,72 depo okumasıdır. Geçersizleştirme kapatılırsa isabet 0,8977’ye döner ama 105.794 bayat okuma oluşur.
  • Önbellek tamamen çöktüğünde uygulama denetimli biçim isteklerin tamamını karşılar ve depoya 513,89 istek/s biner; üzerinden geçen biçimde karşılanan istek sıfırlanır. Kısmi arızada kaybolan silmeler 7473 bayat okuma bırakır.

Sonraki Adım

Bu dersin bütün maliyeti tek bir kısıttan doğdu: önbellek yazmanın olduğu yerde değildi, bu yüzden yeni değeri koyamadı, yalnız eskisini sildi ve her silme bir depo okuması doğurdu. Kısıt her zaman geçerli değildir. Gönderi durumunu depoya yazan yol uygulamanın kendi içinden geçiyorsa önbelleğe yeni değer yazmanın önünde bir engel kalmaz; yazma iki yere birden gider ve silme hiç yapılmaz. Aynı bağ bir adım daha ileri götürülüp yazma önce önbelleğe, depoya sonra toplu olarak da gönderilebilir — bu kez değişen sayı okuma tarafında değil, depoda yazma/okuma orani satırının payındadır. Sonraki ders yazma yolunun önbelleğe bağlandığı bu iki düzeni ölçer: yazma yolunun kaç yere uzadığını, yazma başına ne kadar ek iş çıktığını ve süreç durduğunda kaç olayın bellekte kaldığını.

İ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