---
title: 'Ölçüt Seçimi'
source: 'https://academia.sh/tr/kurslar/islevsel-olmayan-test/olcut-secimi'
course: 'İşlevsel Olmayan Test'
language: tr
updated: '2026-08-17T18:10:51+00:00'
license: 'CC BY-SA 4.0'
---

# Ölçüt Seçimi

Aynı koşumdan yedi ayrı sonuç çıkarılması: ortalama ile ortancanın eşiği geçerken kuyruk yüzdeliklerinin kalması, bir yüzdeliğin okunabilmesi için gereken en az örnek sayısının alt örnek taramasıyla sayılması ve ulaşılan hız raporlanmadan bir gecikme sayısının okunamayacağının hedef hız merdiveniyle gösterilmesi.

Önceki iki ders her kararı tek bir sayıya bağladı: p95. Bu seçim hiç tartışılmadı. Ortalama
alınsaydı aynı koşumlar bambaşka görünürdü, p99 seçilseydi karar yine değişirdi ve üç
saniyelik bir koşumda p99'un kaç örneğe dayandığı sorulmadı. Bu ders ölçütün kendisini bir
karar olarak ele alır.

## Ölçüt Bir Seçimdir

Bir koşumun ürettiği şey tek bir sayı değil, binlerce gecikme değeridir. Ölçüt, bu
dağılımdan tek bir sayı çıkaran işlevdir ve seçim eşiğin anlamını değiştirir. **Ortalama**
bütün örnekleri eşit ağırlıkla toplar; bir tek çok yavaş istek onu az, bin tane orta yavaş
istek çok oynatır. **Yüzdelik gecikme** ise sıralı örnekte bir konumu okur: p99, isteklerin
yüzde birinin daha kötü olduğu değerdir ve kaç kat daha kötü olduklarını hiç söylemez.

Eşik önceki derslerden aynen gelir: **IO1 (varsayım + hesap) — p95 ≤ 20 ms.** Bu derste
eşiğin bir de örnek koşulu yazılır.

**IO5 (hesap) — bir yüzdeliğin karar taşıyabilmesi için koşumun en az $1/(1-p)$ örnek
içermesi gerekir: p95 için 20, p99 için 100, p99,9 için 1.000.** Gerekçe: yüzdeliğin
dışladığı pay en az bir örneğe düşmezse, okunan değer o payın değil, dağılımın daha
alçak bir yerinin değeridir. Bu sayı bir varsayım değil, örnek sayısından çıkan bir alt
sınırdır ve aşağıda taramayla sınanır.

Kararın sahibi ölçüt seçimidir: p99'a bağlanan bir eşik, koşum süresine de bir alt sınır
koyar. Ölçüt seçmek aynı zamanda testin ne kadar süreceğini seçmektir.

## Düzenek

Servis önceki dersten aynen gelir: `/ara` her iki yapıda dizinlidir, `/odunc` yalnız sağlam
yapıda. Üreteç bu kez bütün gecikmeleri biriktirip dosyaya yazar; analiz sunucusuz çalışır.

```js
// kutuphane/sunucu.mjs — gise servisi: /ara katalogda kitap arar, /odunc uyenin acik odunc
// sayisini dogrulayip bes kayit yazar. Argumanlar: <baglanti-noktasi> <yapi 0|1>
// 0 = odunc yolunda uye dizini yok (tam tarama), 1 = dizinli.
import { createServer } from "node:http";
import { DatabaseSync } from "node:sqlite";

const UYE = 500, BASLANGIC = 110000, KITAP = 5;  // uye basina 220 kayit; gise islemi 5 kitap
const port = Number(process.argv[2]), yapi = process.argv[3];

if (Number.isInteger(port) === false || ["0", "1"].includes(yapi) === false) {
  console.log("kullanim: node kutuphane/sunucu.mjs <baglanti-noktasi> <yapi 0|1>");
} else {
  const db = new DatabaseSync(":memory:");
  db.exec("CREATE TABLE odunc(no INTEGER PRIMARY KEY, uye_no TEXT, kitap_no TEXT, gun INT)");
  db.exec("CREATE INDEX odunc_kitap ON odunc(kitap_no)");      // arama yolu her iki yapida dizinli
  if (yapi === "1") db.exec("CREATE INDEX odunc_uye ON odunc(uye_no)");
  db.exec("BEGIN");
  const doldur = db.prepare("INSERT INTO odunc(uye_no, kitap_no, gun) VALUES(?,?,?)");
  for (let i = 0; i < BASLANGIC; i += 1) doldur.run(`U-${i % UYE}`, `K-${i % 9000}`, i % 400);
  db.exec("COMMIT");

  const ara = db.prepare("SELECT no, uye_no, gun FROM odunc WHERE kitap_no = ? LIMIT 5");
  const say = db.prepare("SELECT COUNT(*) AS n FROM odunc WHERE uye_no = ?");
  const ekle = db.prepare("INSERT INTO odunc(uye_no, kitap_no, gun) VALUES(?,?,?)");
  let arama = 0, odunc = 0, kayit = BASLANGIC, tarama = 0;

  createServer((gelen, yanit) => {
    const u = new URL(gelen.url, "http://yerel");
    yanit.writeHead(200, { "content-type": "application/json" });
    if (u.pathname === "/olcut") { yanit.end(JSON.stringify({ arama, odunc, kayit, tarama })); return; }
    if (u.pathname === "/ara") {
      arama += 1;
      yanit.end(JSON.stringify({ bulunan: ara.all(u.searchParams.get("kitap") ?? "K-1").length }));
      return;
    }
    odunc += 1;
    tarama += yapi === "0" ? kayit : Math.ceil(Math.log2(kayit));   // dizinsiz yapida tam tarama
    const uye = u.searchParams.get("uye") ?? "U-1";
    const n = say.get(uye).n;
    for (let j = 0; j < KITAP; j += 1) ekle.run(uye, `K-${(odunc + j) % 9000}`, odunc % 400);
    kayit += KITAP;
    yanit.end(JSON.stringify({ uye, odunc: n + 1 }));
  }).listen(port);
}
```

```js
// kutuphane/olc.mjs — acik dongulu uretec; bir kosumun butun gecikmelerini biriktirip
// JSON satiri olarak basar. Argumanlar: <nokta> <yapi 0|1> <hedef hiz> <odunc payi> <sure s>
import { Agent, request } from "node:http";

const [port, yapi, hiz, pay, sure] = process.argv.slice(2).map((x, i) => (i === 1 ? x : Number(x)));
const agent = new Agent({ keepAlive: true, maxSockets: 8192 });
let sira = 0;

function birIstek(kayit) {
  const i = sira++;
  const yol = (i * 9973) % 1000 < pay * 1000 ? `/odunc?uye=U-${i % 500}` : `/ara?kitap=K-${i % 9000}`;
  const bas = performance.now();
  return new Promise((coz) => {
    const r = request({ port, path: yol, agent }, (y) => {
      y.resume(); y.on("end", () => { kayit.push(performance.now() - bas); coz(); });
    });
    r.on("error", () => { kayit.push(performance.now() - bas); coz(); });
    r.end();
  });
}

if (Number.isInteger(port) === false || ["0", "1"].includes(yapi) === false) {
  console.log("kullanim: node kutuphane/olc.mjs <nokta> <yapi 0|1> <hedef hiz> <odunc payi> <sure>");
} else {
  await birIstek([]);                              // isinma istegi
  const kayit = [], ucus = [];
  let borc = 0, sonT = 0;
  const bas = performance.now();
  while (performance.now() - bas < sure * 1000) {
    const t = (performance.now() - bas) / 1000;
    borc += hiz * (t - sonT); sonT = t;
    while (borc >= 1) { borc -= 1; ucus.push(birIstek(kayit)); }
    await new Promise((c) => setTimeout(c, 5));
  }
  await Promise.all(ucus);
  const gecen = (performance.now() - bas) / 1000;
  console.log(JSON.stringify({ yapi: yapi === "0" ? "dizinsiz" : "dizinli", hiz, pay,
    ulasilan: kayit.length / gecen, ms: kayit.map((x) => Math.round(x * 1000) / 1000) }));
}
agent.destroy();
```

Alt örnek taraması, kaydedilen gecikmelerden yerine koyarak örnek çeker; üreteç doğrusal
eşleniktir ve tohumu görünürdür.

```js
// kutuphane/analiz.mjs — kayitli gecikmeler uzerinde olcut secimi, ornek sayisi taramasi ve
// verim-gecikme okumasi. Arguman: <jsonl>
import { readFileSync } from "node:fs";

const ESIK = 20, TOHUM = 20260731, ALT = 200;      // IO1 esigi, gorunur tohum, alt ornek sayisi
const s = readFileSync(process.argv[2], "utf8").trim().split("\n").map((x) => JSON.parse(x));
const bul = (y, h, p) => s.find((r) => r.yapi === y && r.hiz === h && r.pay === p);
const v = (x, n = 2) => x.toFixed(n).replace(".", ",");
const g = (x, n) => String(x).padStart(n);
const ort = (d) => d.reduce((a, b) => a + b, 0) / d.length;
const yuz = (d, p) => { const t = [...d].sort((a, b) => a - b);
  return t[Math.min(t.length - 1, Math.floor((p / 100) * t.length))]; };

const A = bul("dizinsiz", 1700, 0.25), B = bul("dizinli", 1700, 0.25);
const OLCUT = [["ortalama", (d) => ort(d)], ["p50", (d) => yuz(d, 50)], ["p90", (d) => yuz(d, 90)],
  ["p95", (d) => yuz(d, 95)], ["p99", (d) => yuz(d, 99)], ["p99,9", (d) => yuz(d, 99.9)],
  ["en buyuk", (d) => Math.max(...d)]];
console.log(`ayni iki kosum, yedi olcut; esik ${ESIK} ms (IO1), ornek ${A.ms.length} / ${B.ms.length}`);
console.log("olcut     dizinsiz(ms)  karar   dizinli(ms)  karar   dizinsiz/dizinli");
for (const [ad, f] of OLCUT) {
  const a = f(A.ms), b = f(B.ms);
  console.log(ad.padEnd(10) + g(v(a), 12) + g(a <= ESIK ? "gecti" : "kaldi", 7) +
    g(v(b), 13) + g(b <= ESIK ? "gecti" : "kaldi", 7) + g(v(a / b), 18));
}

let t = TOHUM;
const rast = () => { t = (1103515245 * t + 12345) % 2147483648; return t / 2147483648; };
const altOrnek = (d, n) => Array.from({ length: n }, () => d[Math.floor(rast() * d.length)]);
console.log(`\nornek sayisi taramasi: ${ALT} alt ornek, tohum ${TOHUM}; tam ornekte p99 ` +
  `dizinsiz ${v(yuz(A.ms, 99))} ms, dizinli ${v(yuz(B.ms, 99))} ms`);
console.log("ornek  p99 en kucuk  p99 en buyuk  yayilim kat  yanlis gecme/200  yanlis kalma/200");
for (const n of [20, 50, 100, 500, 2000]) {
  const a = Array.from({ length: ALT }, () => yuz(altOrnek(A.ms, n), 99));
  const b = Array.from({ length: ALT }, () => yuz(altOrnek(B.ms, n), 99));
  console.log(g(n, 5) + g(v(Math.min(...a)), 13) + g(v(Math.max(...a)), 14) +
    g(v(Math.max(...a) / Math.min(...a)), 13) + g(a.filter((x) => x <= ESIK).length, 18) +
    g(b.filter((x) => x > ESIK).length, 18));
}

console.log("\nverim-gecikme: dizinsiz yapi, ayni karisim, dort hedef hiz");
console.log("hedef hiz  ulasilan/s  ulasilan/hedef  p95(ms)  karar");
for (const h of [900, 1300, 1700, 2100]) {
  const r = bul("dizinsiz", h, 0.25);
  console.log(g(h, 9) + g(v(r.ulasilan), 12) + g(v(r.ulasilan / h, 3), 16) +
    g(v(yuz(r.ms, 95)), 9) + g(yuz(r.ms, 95) <= ESIK ? "gecti" : "kaldi", 7));
}
```

```bash
# olc.sh — bes kosum: ayni senaryo iki yapida (5 s), arti dizinsiz yapida uc ek hedef hiz (2 s).
rm -f sonuc.jsonl
for Y in 0 1; do
  node kutuphane/sunucu.mjs 8853 $Y & SUNUCU=$!
  sleep 1.2
  node kutuphane/olc.mjs 8853 $Y 1700 0.25 5 >> sonuc.jsonl
  kill $SUNUCU
  wait $SUNUCU 2>/dev/null
done
for H in 900 1300 2100; do
  node kutuphane/sunucu.mjs 8853 0 & SUNUCU=$!
  sleep 1.2
  node kutuphane/olc.mjs 8853 0 $H 0.25 2 >> sonuc.jsonl
  kill $SUNUCU
  wait $SUNUCU 2>/dev/null
done
node kutuphane/analiz.mjs sonuc.jsonl
```

```
ayni iki kosum, yedi olcut; esik 20 ms (IO1), ornek 8494 / 8492
olcut     dizinsiz(ms)  karar   dizinli(ms)  karar   dizinsiz/dizinli
ortalama          9,04  gecti         1,87  gecti              4,84
p50               8,66  gecti         1,64  gecti              5,28
p90              16,09  gecti         3,02  gecti              5,33
p95              18,33  gecti         3,54  gecti              5,18
p99              25,35  kaldi         7,77  gecti              3,26
p99,9            98,38  kaldi        15,58  gecti              6,31
en buyuk        103,88  kaldi        16,05  gecti              6,47

ornek sayisi taramasi: 200 alt ornek, tohum 20260731; tam ornekte p99 dizinsiz 25,35 ms, dizinli 7,77 ms
ornek  p99 en kucuk  p99 en buyuk  yayilim kat  yanlis gecme/200  yanlis kalma/200
   20        13,03         38,18         2,93                86                 0
   50        17,62         38,18         2,17                15                 0
  100        21,68         38,18         1,76                 0                 0
  500        27,54         34,50         1,25                 0                 0
 2000        27,54         27,54         1,00                 0                 0

verim-gecikme: dizinsiz yapi, ayni karisim, dort hedef hiz
hedef hiz  ulasilan/s  ulasilan/hedef  p95(ms)  karar
      900      896,85           0,996    11,31  gecti
     1300     1291,61           0,994    13,89  gecti
     1700     1695,88           0,998    18,33  gecti
     2100     1470,45           0,700  1381,94  kaldi
```

## Ortalamanın Gizlediği

Birinci tablo tek bir koşumdan yedi sayı çıkarıyor ve yedisi aynı yapı için **iki ayrı
karar** veriyor. Bu koşumda ortalama 9,04, ortanca 8,66, p90 16,09 ve p95 18,33 eşiğin
altında; p99 25,35, p99,9 98,38 ve en büyük 103,88 üstünde. Kod aynı, koşum aynı, örnek aynı;
değişen tek şey dağılımdan hangi noktanın okunduğu. Sayılar ölçüm sınıfındadır ve bu makineye
bağlıdır; **kesme noktasının hangi ölçüt çiftinin arasına düştüğü koşumdan koşuma oynar** —
başka bir koşumda p95 de eşiğin üstüne çıkabilir. Koşumdan bağımsız olan, sıranın kendisidir:
kuyruğa doğru gidildikçe okunan değer büyür ve dizinsiz yapının sağlam yapıya oranı artar.

Ortalamanın niçin geçtiği son sütunda görünüyor. Bu koşumda isteklerin dörtte biri ödünç
yoluna gidiyor ve tam tarama yapıyor; kalan dörtte üçü ucuz aramalar. Ortalama iki kümeyi
birbirine karıştırır ve pahalı olanın ağırlığını dörtte bire indirir. Kuyruk yüzdelikleri
karıştırmaz: p99 tanımı gereği yalnız en yavaş yüzde birde durur ve orada dizinsiz yapı
sağlam yapının bu koşumda 3,26 katıdır, p99,9'da 6,31 katı. Katın tam değeri koşuma bağlıdır;
koşumdan bağımsız olan, oranın kuyruğa doğru gidildikçe büyümesidir.

Yakalanan kusur sınıfının adı **kuyruk gecikmesidir**. Sağlam yapı yedi ölçütün yedisinde
de geçiyor, dolayısıyla bu koşumda p99'un yanlış kalma üretmediği de görülüyor: ölçütü
sıkılaştırmak sağlam yapıyı kırmızıya döndürmedi.

## Bir Yüzdelik Kaç Örneğe Dayanır

İkinci tablo aynı gecikmelerden alt örnekler çekiyor ve her boyda p99'u yeniden hesaplıyor.
Tam örnekte p99 25,35 milisaniye; yirmi örneklik alt örneklerde aynı sayı 13,03 ile 38,18
arasında geziniyor, yani **2,93 kat yayılım**. Kararı bozan yay budur: iki yüz alt örneğin
86'sında p99 eşiğin altında çıkıyor ve koşum **yanlış geçme** veriyor. Elli örnekte bu sayı
15'e, yüz örnekte sıfıra iniyor.

Sıfırın yüz örnekte gelmesi rastlantı değil, IO5'in kendisidir: p99'un dışladığı pay yüzde
bir olduğu için yüz örnekten azında o paya tek bir istek bile düşmeyebilir ve okunan değer
dağılımın daha alçak bir yerinden gelir. Beş yüz ve iki bin örnekte yayılım 1,25 ve 1,00
kata iniyor. Yanlış kalma sütunu bütün boylarda sıfır: sağlam yapının p99'u eşikten o kadar
uzak ki alt örnekleme onu eşiğin üstüne çıkaramıyor. **Örnek sayısı yalnız yanlış geçme
tarafını bozuyor** — az örnek, hep iyimser bir sonuç üretir.

Buradan çıkan kural koşum süresine bakar: hedef hız 1.700 istek/s ise p99 için gereken yüz
örnek 0,06 saniyede toplanır, ama tepe saatin dışında 10 istek/s ile koşan bir ortamda aynı
sayı 10 saniye ister. p99,9 için bu süre bin katına çıkar.

## Verim Okunmadan Gecikme Okunmaz

Üçüncü tablo aynı yapıyı dört hedef hızda koşturuyor. İlk üç satırda ulaşılan hız hedefin
0,994 ile 0,998'i arasında; p95 sırasıyla 11,31, 13,89 ve 18,33 milisaniye. Bu üç sayı
karşılaştırılabilir, çünkü üçü de istenen yükün altında ölçüldü.

Dördüncü satır karşılaştırılabilir değildir. Hedef 2.100 istek/s, ulaşılan 1.470,45 — yani
**hedefin 0,700'ü**. p95 1.381,94 milisaniye görünüyor ama bu sayı 2.100 istek/s'in p95'i
değil, sistemin kabul edebildiği 1.470 istek/s'in p95'idir; saniyede 630 istek hiç
karşılanmadı. Yalnız gecikme sütununu taşıyan bir rapor bu satırı "yavaş" diye okur, oysa
söylediği şey başkadır: **sistem istenen yüke ulaşamadı.** Gecikme ile verim tek bir çift
olarak okunur; ulaşılan hız raporlanmayan bir gecikme sayısı hangi yüke ait olduğunu
söylemez.

## Bedel ve Kaçan Sınıf

Bedelin koşuma bağlı yanı ortama bağlıdır: beş koşum bu makinede yarım dakikanın altında
sürdü. Koşumdan bağımsız yanı sayılır — iki analiz koşumu 8.494 ve 8.492 gecikme değeri
biriktirdi, alt örnek taraması beş boyda iki yüzer kez, yani 1.000 yeniden hesap yaptı ve
toplam 536.000 örnek çekti. Kaçan sınıf da ölçüldü: ortalama ve ortanca bu kusuru yedi
ölçütün dördünde geçirdi, yani bir ölçüt kümesinin sınırı da yanlış geçme sayısıyla yazılır.

## Özet

- Ölçüt bir seçimdir: aynı koşumda ortalama 9,04 ve p50 8,66 eşiği geçerken p99 25,35 ve
  p99,9 98,38 kalıyor.
- Ortalama pahalı ile ucuz isteği karıştırır; kuyruk yüzdeliği karıştırmaz ve iki yapıyı
  p99'da 3,26, p99,9'da 6,31 kat ayırır.
- Yüzdelik örnek sayısına duyarlıdır: yirmi örnekte p99 tahmini 2,93 kat yayılıyor ve 200
  alt örneğin 86'sı yanlış geçme veriyor; yüz örnekte bu sayı sıfırlanıyor.
- IO5 bunu bir alt sınıra bağlar: bir yüzdelik en az $1/(1-p)$ örnek ister, yani p99 için
  100, p99,9 için 1.000. Az örnek yalnız iyimser yönde yanılır.
- Gecikme verimle birlikte okunur: 2.100 istek/s hedefinde ulaşılan hız 0,700'e düştüğü
  için 1.381,94 milisaniyelik p95 istenen yükün değil, kabul edilen yükün sayısıdır.

## Sonraki Adım

Ölçüt seçildi ve eşik kırmızıya döndü. Rapor artık şunu söylüyor: p99 25,35 milisaniye,
eşik 20, kaldı. Bu cümle bir kusuru bildirir ama **nerede** olduğunu söylemez. Aynı sayı
uygulama katmanındaki bir hesaptan, veritabanındaki bir taramadan ya da yanıtın
büyüklüğünden doğmuş olabilir ve üçünün düzeltmesi birbirinden başkadır. Sonraki ders bu
atfetmeyi ele alır: bir yük testi sonucu hangi ölçümle bir katmana bağlanır, aynı belirtiyi
veren iki katman nasıl ayrılır ve atfetme ne zaman yanlıştır.
