Ders 03 / 19
Ölçüt Türleri
Aynı olgunun sayaç, ölçer ve histogramla ölçülmesi: her tipin yanıtladığı ve kapattığı soru, ölçerin okumalar arasında kaybettiği gözlem sayısı, histogramın kova sayısına göre bellek maliyeti ve yüzdelik hatası.
İçindekiler
Önceki iki ders günlüğü olgunlaştırdı: kayıt alanlara bölündü, bir istek dört serviste izlenir oldu. Ama her sorgu hâlâ bütün kayıtları okumakla başlıyor. İlk derste ölçüt bu maliyetin dışındaydı, çünkü sorulacak soru kayıt yazılırken seçilmişti. Bu ders o seçime bakar: aynı olgu üç ölçüt tipiyle ölçülür ve her tipin hangi soruyu açtığı, hangisini kapattığı sayılır.
Ölçülen olgu tektir: açık ödünç sayısı. Her istek bir ödünç açar, aralarda toplu iadeler ve seyrek toplu ödünç günleri olur. Aynı sayı üç tiple ölçülür. Sayaç yalnız artar. Ölçer o anki değeri tutar. Histogram değerleri kovalara dağıtır. Üçü de aynı olgudan beslenir, üçü de farklı bir soruyu yanıtlar.
Enstrümantasyonun kendi bedeli — ölçüt üretmenin isteğe eklediği yük — Başarım Karşıt Kalıpları ve İzleme kursunda ölçüldü ve burada tekrarlanmaz. Buradaki bedel bellek ve doğruluktur: bir tipin kaç alan tuttuğu ve yanıtının gerçekten ne kadar uzakta olduğu.
Üç Tip
// olcutler.mjs — uc olcut tipi; ucu de elle yazildi, disaridan hicbir sey gelmiyor export class Sayac { // yalniz artar; gecmis tek sayida toplanir constructor() { this.deger = 0; } art(n = 1) { this.deger += n; } oku() { return this.deger; } } export class Olcer { // o anki degeri tutar; onceki deger kaybolur constructor() { this.deger = 0; } yaz(v) { this.deger = v; } oku() { return this.deger; } } export class Histogram { // kova sayimlari; dagilim yaklasik olarak korunur constructor(sinir) { this.sinir = sinir; this.kova = new Array(sinir.length + 1).fill(0); this.adet = 0; } gozle(v) { let i = 0; while (i < this.sinir.length && v > this.sinir[i]) i += 1; this.kova[i] += 1; this.adet += 1; } alan() { return this.kova.length; } yuzdelik(p) { // hedefi asan kovanin icinde dogrusal aradegerleme const hedef = p * this.adet; let birikim = 0; for (let i = 0; i < this.kova.length; i += 1) { if (birikim + this.kova[i] >= hedef) { const alt = i === 0 ? 0 : this.sinir[i - 1]; const ust = i < this.sinir.length ? this.sinir[i] : alt * 2; return alt + ((hedef - birikim) / this.kova[i]) * (ust - alt); } birikim += this.kova[i]; } return this.sinir[this.sinir.length - 1]; } } export const esitSinir = (k, enBuyuk) => // k kova: [0, enBuyuk] araligi esit bolunur Array.from({ length: k - 1 }, (_, i) => Math.round(((i + 1) * enBuyuk) / (k - 1)));
Üç sınıfın toplam durumu şudur: sayaç bir sayı, ölçer bir sayı, histogram kova sayısı kadar sayı. Tiplerin farkı davranışlarında değil, neyi unuttuklarındadır. Sayaç her gözlemi toplama katıp gözlemi unutur. Ölçer son gözlemi tutup öncekini unutur. Histogram gözlemin hangi kovaya düştüğünü tutup değerin kendisini unutur.
TL3 — açık ödünç sayısının değişim deseni koşumdan bağımsızdır: iade ve toplu ödünç kararları tohumu görünen bir üreteçten gelir. Gerekçe: karşılaştırılan şey ölçüt tiplerinin davranışıdır, ortamın hızı değil; aynı gözlem dizisi her koşumda üretilmezse hata payları karşılaştırılamaz.
Aynı Olgu, Üç Tip
// servis3.mjs — odunc servisi; ayni olguyu (acik odunc sayisi) uc tiple olcer; uzun omurlu surec import { createServer } from "node:http"; import { Sayac, Olcer, Histogram, esitSinir } from "./olcutler.mjs"; const acilan = new Sayac(), acik = new Olcer(), dagilim = new Histogram(esitSinir(8, 400)); let tohum = 20260731; // gorunur tohum: iade deseni kosumdan bagimsiz const sonraki = () => (tohum = (tohum * 1103515245 + 12345) % 2147483648); let deger = 0, tepe = 0; // tepe: olcerin kacirdigini gostermek icin tutuluyor const ornek = []; // tam orneklem: yalniz karsilastirma icin saklaniyor createServer((istek, yanit) => { if (istek.url === "/olcut") { yanit.end(JSON.stringify({ acilan: acilan.oku(), acik: acik.oku(), kova: dagilim.kova, sinir: dagilim.sinir, tepe })); return; } if (istek.url === "/ornek") { yanit.end(JSON.stringify(ornek)); return; } deger += 1; acilan.art(); // her istek bir odunc acar if (sonraki() % 60 === 0) deger = Math.max(0, deger - Math.ceil(deger / 2)); // toplu iade if (sonraki() % 211 === 0) deger += 180; // toplu odunc gunu tepe = Math.max(tepe, deger); acik.yaz(deger); dagilim.gozle(deger); ornek.push(deger); yanit.end("bitti"); }).listen(9301);
Servis ayrıca iki şeyi karşılaştırma için tutuyor: gerçek tepe değeri ve tam örneklem. İkisi de gerçek bir sistemde tutulmaz — tam da bu dersin ölçtüğü maliyet yüzünden. Burada duruyorlar ki üç tipin verdiği yanıt gerçeğiyle karşılaştırılabilsin.
Ölçüm
Sorgu aracı iki şey yapar. Önce üç tipi yan yana koyup her birinin yanıtladığı ve yanıtlayamadığı soruyu yazar. Sonra aynı gözlem dizisini dört, sekiz, on altı, otuz iki ve altmış dört kovalı histogramlarla yeniden özetleyip her birinin bellek maliyetini ve p95 hatasını çıkarır.
// sor3.mjs — uc tipin yanitladigi soru ve histogram kova sayisinin bedeli import { Histogram, esitSinir } from "./olcutler.mjs"; const m = await (await fetch("http://127.0.0.1:9301/olcut")).json(); const ornek = await (await fetch("http://127.0.0.1:9301/ornek")).json(); const sirali = [...ornek].sort((a, b) => a - b); const gercek = (p) => sirali[Math.floor(p * sirali.length)]; const h = new Histogram(m.sinir); h.kova = m.kova; h.adet = ornek.length; console.log(`${"tip".padEnd(11)}${"alan".padStart(5)} ${"yanitladigi soru".padEnd(36)}yanitlayamadigi soru`); console.log(`${"sayac".padEnd(11)}${"1".padStart(5)} ${`kac odunc acildi: ${m.acilan}`.padEnd(36)}su an kac odunc acik`); console.log(`${"olcer".padEnd(11)}${"1".padStart(5)} ${`su an kac odunc acik: ${m.acik}`.padEnd(36)}gecmiste tepe neydi`); console.log(`${"histogram".padEnd(11)}${String(m.kova.length).padStart(5)} ` + `${`gozlemlerin %95'i ${Math.round(h.yuzdelik(0.95))} altinda`.padEnd(36)}hangi gozlem tepe yapti`); console.log(`gercek p95 ${gercek(0.95)}, gercek tepe ${m.tepe}; ` + `tam orneklem ${ornek.length} deger x 8 bayt = ${ornek.length * 8} bayt`); console.log(`\n${"kova".padStart(5)}${"bellek".padStart(9)}${"p95 tahmin".padStart(12)}${"hata".padStart(7)}${"bagil".padStart(8)}`); for (const k of [4, 8, 16, 32, 64]) { const hh = new Histogram(esitSinir(k, 400)); for (const v of ornek) hh.gozle(v); const t = hh.yuzdelik(0.95), hata = Math.abs(t - gercek(0.95)); console.log(`${String(k).padStart(5)}${`${hh.alan() * 8} bayt`.padStart(9)}${t.toFixed(1).padStart(12)}` + `${hata.toFixed(1).padStart(7)}${`%${((100 * hata) / gercek(0.95)).toFixed(1)}`.padStart(8)}`); }
cat > kos.mjs <<'EOF' const OKUMA = 100; // olcer her 100 istekte bir okunur let enYuksek = 0, okuma = 0; for (let i = 1; i <= 2000; i += 1) { await fetch("http://127.0.0.1:9301/is"); if (i % OKUMA !== 0) continue; const m = await (await fetch("http://127.0.0.1:9301/olcut")).json(); enYuksek = Math.max(enYuksek, m.acik); okuma += 1; } const m = await (await fetch("http://127.0.0.1:9301/olcut")).json(); console.log(`olcer: 2000 gozlem, ${okuma} okuma, okunan en yuksek ${enYuksek}, ` + `gercek tepe ${m.tepe}; okumalar arasinda gorulmeyen gozlem ${2000 - okuma}`); EOF node servis3.mjs & sleep 1 node kos.mjs echo node sor3.mjs pkill -f "node servis3.mjs"
olcer: 2000 gozlem, 20 okuma, okunan en yuksek 199, gercek tepe 293; okumalar arasinda gorulmeyen gozlem 1980
tip alan yanitladigi soru yanitlayamadigi soru
sayac 1 kac odunc acildi: 2000 su an kac odunc acik
olcer 1 su an kac odunc acik: 26 gecmiste tepe neydi
histogram 8 gozlemlerin %95'i 159 altinda hangi gozlem tepe yapti
gercek p95 159, gercek tepe 293; tam orneklem 2000 deger x 8 bayt = 16000 bayt
kova bellek p95 tahmin hata bagil
4 32 bayt 209.1 50.1 %31.5
8 64 bayt 158.5 0.5 %0.3
16 128 bayt 157.1 1.9 %1.2
32 256 bayt 161.5 2.5 %1.6
64 512 bayt 158.5 0.5 %0.3
Her Tip Bir Soruyu Kapatıyor
Üst tablo tiplerin bölüşümünü veriyor. Sayaç iki bin ödüncün açıldığını tek alanda söylüyor, ama kaçının kapandığını bilmediği için “şu an kaç ödünç açık” sorusuna yanıt veremiyor — artışın geri alınamaması sayacın tanımıdır. Ölçer o soruyu tek alanda yanıtlıyor: yirmi altı. Histogram ikisinin de yanıtlayamadığını yanıtlıyor: gözlemlerin yüzde doksan beşi 159’un altında kalmış.
Ölçerin bedeli ilk satırda duruyor ve dersin en keskin sayısı odur. İki bin gözlem üretildi, ölçer yirmi kez okundu, okunan en yüksek değer 199 çıktı — gerçek tepe ise 293. Okumalar arasında kalan bin dokuz yüz seksen gözlem hiç görülmedi ve tepe yüzde otuz iki eksik raporlandı. Ölçerin tuttuğu şey “o anki değer” olduğu için, okuma sıklığı ne olursa olsun okumalar arasındaki hareket kaybolur. Bir eşiğin aşılıp aşılmadığı ölçerle sorulduğunda, yanıtın “hayır” olması eşiğin aşılmadığı değil, aşıldığı ana denk gelinmediği anlamına gelebilir.
Alt tablo histogramın kova sayısını bedele bağlıyor. Dört kova otuz iki bayt tutuyor ve p95’i 209 diye veriyor: 159 olan gerçeğe göre yüzde otuz bir buçuk sapma. Sekiz kovada hata yarım birime, yüzde 0,3’e düşüyor. Buradan sonrası şaşırtıcıdır ve dersin ikinci bulgusudur: hata kova sayısıyla düzgün azalmıyor. On altı kovada 1,9, otuz iki kovada 2,5, altmış dörtte yine 0,5. Neden açıktır — hata, kova sınırlarının dağılımın hangi noktasına düştüğüne bağlıdır. Yüzde doksan beşinci yüzdelik 159 civarındadır; bir sınır oraya yakın düşerse aradeğerleme kısa mesafede yapılır ve hata küçülür, sınır uzaksa kovanın içi doğrusal varsayılır ve hata büyür. Kova sayısını artırmak beklenen hatayı düşürür, ama tek bir yüzdelik için garanti vermez.
Karşılaştırma çizgisi son satırdadır: tam örneklem 16.000 bayt tutuyor ve p95’i kesin veriyor. Sekiz kovalı histogram 64 bayt tutuyor ve aynı yanıtı yarım birim hatayla veriyor — iki yüz elli kat daha az bellekle. Ölçütün asıl işi budur: cevabı yaklaşık ama sabit maliyetle tutmak. Sabit maliyet, önceki dersteki günlük hacminin istek sayısıyla büyümesinin karşıtıdır.
Özet
- Aynı olgu üç tiple ölçüldü: sayaç 2000 açılışı, ölçer 26 açık ödüncü, histogram gözlemlerin %95’inin 159 altında olduğunu verdi; hiçbiri diğerinin sorusunu yanıtlamadı.
- Tiplerin farkı neyi unuttuklarındadır: sayaç gözlemi, ölçer önceki değeri, histogram değerin kendisini unutur.
- Ölçer 2000 gözlemin 20’sini gördü; okunan tepe 199, gerçek tepe 293 — okumalar arasındaki 1980 gözlem kayboldu ve tepe eksik raporlandı.
- Histogramın kova sayısı belleği doğrusal büyütüyor (4 kova 32 bayt, 64 kova 512 bayt), ama hata düzgün azalmıyor: %31,5 → %0,3 → %1,2 → %1,6 → %0,3.
- 8 kovalı histogram, 16.000 baytlık tam örneklemin verdiği p95’i 64 baytla ve yarım birim hatayla verdi; ölçütün maliyeti gözlem sayısıyla değil alan sayısıyla büyür.
Sonraki Adım
Üç sinyalin ikisi artık kodda üretiliyor: alan tabanlı günlük bir isteği dört serviste izliyor, ölçütler sabit maliyetle özet veriyor. Geriye ilk dersin yanıtsız bıraktığı asıl soru kalıyor. Bağıntı kimliği bir isteğin hangi servislerden geçtiğini söylüyor, ama sürelerin nasıl iç içe geçtiğini söylemiyor: katalog çağrısı ödünç isteğinin içinde mi başladı, üyelik çağrısından önce mi bitti, bekleme nerede oldu. Sonraki ders iz bağlamını kendi yazar ve süreç sınırından geçirir. Ölçülecek olanlar bellidir: bağlamın taşınmadığı yerde kopan iz sayısı, örnekleme oranının yakalanan sorunlu istek yüzdesine etkisi ve iz kaydının hacmi.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.