Ders 07 / 13
Başarım ve Ölçeklenebilirlik
İki kavramın yük altında ayrışması: başarımın belirli bir yükteki ölçüt değeri, ölçeklenebilirliğin ölçütün yükle değişimi olduğunun gösterilmesi, gün sonu işinin iki gerçekleştiriminin dört yükte ölçülmesi, eşiği karşılayan en büyük yükün kapasite olarak hesaplanması ve sabit çarpan kazancının mertebe kazancından ayrılması.
İçindekiler
Önceki ders tek bir süreci sabit tutup yükü değiştirdi ve ortaya iki bölge çıktı: verimin eşzamanlılıkla büyüdüğü bölge ile verimin bir üst sınıra dayanıp gecikmenin büyüdüğü bölge. Aynı sistemin iki ayrı yükte iki ayrı davranış göstermesi, tek bir ölçümün sistem hakkında ne kadar az şey söylediğini de gösterdi.
Buradan iki soru çıkar. Belirli bir yükte sistem ne kadar iyidir, ve yük büyüdüğünde bu iyilik ne kadar dayanır. Bu ders iki soruyu ayrı adlarla karşılar ve farkı gün sonu ücretlendirme işi üzerinde sayar. Ayrımı görünür kılmanın tek yolu birden fazla yükte ölçmektir; tek bir yükteki ölçüm, hangi soruya yanıt verdiğini bile söylemez.
Bir Nokta ile Bir Eğri
Başarım (performance) belirli bir yükte ölçülen ölçüt değeridir: “bu iş bu makinede bu kadar kayıtla 460 milisaniye sürüyor” bir başarım ifadesidir. Tek bir noktadır ve o noktanın dışında hiçbir şey söylemez.
Ölçeklenebilirlik (scalability) ölçütün yükle birlikte nasıl değiştiğidir: yük iki katına çıktığında sürenin kaç katına çıktığı. Bir nokta değil, bir eğridir. Bir sistemin ölçeklenebilirliği hakkında tek bir ölçümden hiçbir şey çıkarılamaz, çünkü bir noktadan eğri geçirilemez.
İki kavramın karıştırılması pratik bir yanlışa yol açar: başarımı iyileştiren bir değişiklik ölçeklenebilirliği iyileştirmiş sayılır. Aşağıdaki ölçüm bunun neden yanlış olduğunu gösteriyor. Kalite Nitelikleri dersinin nitelik aileleri listesinde başarım, başarım verimliliği ailesine girer; ölçeklenebilirlik listede yoktur ve liste kapalı olmadığı için bu alanda ayrı bir aile olarak eklenir. İkisinin nitelik senaryolarında da ölçüm parçası farklıdır: biri sabit bir yükte eşik, öteki yükün büyümesi karşısında davranış belirtir.
Aynı İşin İki Gerçekleştirimi
Gün sonu ücretlendirmesi kalemleri fatura satırlarında toplar. Bir fatura satırı bir satıcının bir gününe karşılık gelir; bundan sonra bu gruplama birimine bölüm denecek ve satıcı numarası ile gün birlikte bölüm anahtarını verecektir. İki gerçekleştirim aynı sonucu verir, farklı yollardan. A, her bölüm için bütün kalemleri tarar. B, kalemleri bir kez geçer ve bölüm anahtarını dizin olarak kullanır.
Kalem sayısı ile bölüm sayısı bağımsız değildir. Oranı veren varsayım Kabaca Büyüklük Hesabı dersinin tablosunda V13 olarak duruyor: bir fatura satırına 100 kalem toplanır. Buradan V3 ile tutan bir zincir çıkar; günde 400 000 gönderi bu oranla günde 4000 fatura satırı, yani 4000 satıcı demektir. Otuz günü tarayan ücretlendirme işi bu yüzden 120 000 bölüm görür: 4000 satıcı çarpı 30 gün. Varsayımın duyarlılığı hesapta gösterilecek.
// toplu/gruplama.mjs — gun sonu ucretlendirmesinin iki gerceklestirimi ayni dort yukte olculur const TARIFE = { kademe: [[1, 3000], [5, 4800], [20, 9600]], bolge: { "34": 100, "06": 115, "35": 125 }, asgari: 2500 }; const BOLGE = ["34", "06", "35"]; const BOLUM_BASINA = 100; // V13: bir fatura satirina toplanan kalem sayisi function kalemler(n, bolum) { const d = new Array(n); for (let i = 0; i < n; i += 1) { d[i] = { bolum: i % bolum, agirlik: (i % 20) + 1, bolge: BOLGE[i % 3], oran: (i % 5) / 20 }; } return d; } function net(k) { const kademe = TARIFE.kademe.find(([s]) => k.agirlik <= s) ?? [0, 9600]; const taban = Math.max(TARIFE.asgari, Math.round((kademe[1] * TARIFE.bolge[k.bolge]) / 100)); return taban - Math.round(taban * Math.min(k.oran, 0.4)); } function faturaA(veri, bolum) { // her bolum icin butun kalemler taranir const cikti = new Array(bolum).fill(0); for (let b = 0; b < bolum; b += 1) { for (const k of veri) if (k.bolum === b) cikti[b] += net(k); } return cikti; } function faturaB(veri, bolum) { // tek gecis, bolum anahtariyla toplanir const cikti = new Array(bolum).fill(0); for (const k of veri) cikti[k.bolum] += net(k); return cikti; } function olc(f, veri, bolum) { const bas = performance.now(); const r = f(veri, bolum); return { ms: performance.now() - bas, imza: r[0] + r[bolum - 1] }; } // A kat: yuk ikiye katlanirken surenin kac katina ciktigi. A mertebe: log2(kat) — 1 dogrusal, // 2 kare buyume. Ham ms makineye baglidir, mertebe ise gerceklestirimin ozelligidir. console.log("kalem bolum A(ms) B(ms) A/B A us/kalem B us/kalem A kat mertebe ayni"); let oncekiA = 0; for (const n of [20_000, 40_000, 80_000, 160_000]) { const bolum = n / BOLUM_BASINA; const veri = kalemler(n, bolum); const a = olc(faturaA, veri, bolum); const b = olc(faturaB, veri, bolum); const kat = oncekiA === 0 ? null : a.ms / oncekiA; oncekiA = a.ms; console.log(`${String(n).padStart(7)}${String(bolum).padStart(10)}` + `${a.ms.toFixed(1).padStart(11)}${b.ms.toFixed(1).padStart(10)}${(a.ms / b.ms).toFixed(0).padStart(8)}` + `${((a.ms * 1000) / n).toFixed(2).padStart(12)}${((b.ms * 1000) / n).toFixed(2).padStart(12)}` + `${(kat === null ? "-" : kat.toFixed(2)).padStart(8)}${(kat === null ? "-" : Math.log2(kat).toFixed(2)).padStart(9)}` + `${String(a.imza === b.imza).padStart(6)}`); }
kalem bolum A(ms) B(ms) A/B A us/kalem B us/kalem A kat mertebe ayni 20000 200 13.3 1.4 9 0.67 0.07 - - true 40000 400 32.2 0.7 45 0.81 0.02 2.42 1.28 true 80000 800 121.9 1.8 66 1.52 0.02 3.78 1.92 true 160000 1600 471.7 2.9 160 2.95 0.02 3.87 1.95 true
Milisaniye sütunları ölçümdür ve bu makinede alınmıştır; başka bir makinede başka değerler
çıkar. Son iki sütun bu yüzden eklenmiştir: A kat ile mertebe, ham sürelerden türetilmiş
büyüklüklerdir ve makineden makineye değişmezler. ayni sütunu iki gerçekleştirimin aynı
toplamları ürettiğini doğruluyor; farklı sonuç veren iki gerçekleştirimin süreleri
karşılaştırılamaz.
Tablonun Söylediği
Mutlak süreler dördüncü ve beşinci sütunda değil, kalem başına sütunlarında okunur.
B’nin kalem başına süresi dört yükte 0,07 ve üç kez 0,02 mikrosaniyedir; ilk satırdaki fark ısınmadan gelir, sonrası sabittir. Yük sekiz katına çıktığında kalem başına süre değişmiyor: işin maliyeti kalem sayısıyla doğru orantılı.
A’nın kalem başına süresi bu koşumda 0,67’den 2,95 mikrosaniyeye çıkıyor. Yük sekiz katına
çıkarken kalem başına maliyet de büyüyor, çünkü her kalem daha çok bölüm için taranıyor. Asıl
okuma mertebe sütunundadır. Değer 1 olsaydı süre yükle doğru orantılı büyüyordu; 2 olması
sürenin yükün karesiyle büyüdüğünü gösterir. Ölçümde son iki ikiye katlamada 1,92 ve 1,95 çıkıyor,
yani yük iki katına çıktığında süre dört katına çıkıyor. İlk ikiye katlamada değer 1,28’de
kalıyor; sebebi en küçük yükte çalışma zamanının henüz ısınmamış olmasıdır ve aşağıdaki hesap
katsayıyı bu yüzden en küçük değil en büyük ölçümden alır. Mutlak süreler makineye göre değişir,
mertebe değişmez — sütunun eklenme nedeni budur.
Şimdi kritik gözlem. İlk satırda A ile B arasındaki oran 10 dolayındadır ve A’nın mutlak süresi on beş milisaniyeyi geçmez — bir toplu iş için hiç sorun değil. Bu yükte alınmış bir başarım ölçümü A’yı kabul edilebilir bulur ve etmekle haklıdır. Dördüncü satırda oran yüzü aşar (bu koşumda 160). Aynı sistem, aynı kod, aynı makine; değişen tek şey yük. Oranın kendisi yükün bir işlevidir, dolayısıyla “A, B’den şu kadar yavaştır” cümlesi yüksüz söylendiğinde anlamsızdır.
Kapasite
Eğriyi karara çeviren şey bir eşiktir. Kabaca Büyüklük Hesabı dersinde gün sonu işinin dört saatlik bir pencerede koştuğu ve otuz günü kapsayan 12 milyon kaydı taradığı hesaplanmıştı. Kapasite, eşiği karşılayan en büyük yüktür. Aşağıdaki hesap ölçülen katsayıları alır, iki gerçekleştirimin süresini bugünün ve iki ile dört katı hacmin altında bulur, sonra her birinin kapasitesini çıkarır.
// hesap/kapasite.mjs — olculen katsayilardan dort saatlik pencerede kapasite ve sabit carpan kazancinin etkisi const PENCERE = 4 * 3600 * 1000; // V10: toplu is penceresi (ms) const GUNLUK = 400_000; // V3: gunde olusan yeni gonderi const GUN = 30; // V11: ucretlendirmenin taradigi gun const BUGUN = GUNLUK * GUN; // taranan kayit const BOLUM_BASINA = 100; // varsayim: bir fatura satirina toplanan kalem sayisi console.log(`taranan kayit = ${BUGUN}, gunluk bolum = ${GUNLUK / BOLUM_BASINA} (= satici), ` + `${GUN} gunde bolum = ${BUGUN / BOLUM_BASINA}`); const OLCUM = { n: 160_000, bolum: 1_600, aMs: 471.7, bMs: 2.9 }; // toplu/gruplama.mjs son satiri const aBirim = OLCUM.aMs / (OLCUM.n * OLCUM.bolum); // ms / kontrol const bBirim = OLCUM.bMs / OLCUM.n; // ms / kalem console.log(`girdi: bu makinedeki bir kosumun olcumu (A ${OLCUM.aMs} ms, B ${OLCUM.bMs} ms, ` + `n = ${OLCUM.n}); asagidaki butun sureler ve kapasiteler bu iki sayiya baglidir`); const aSure = (n) => aBirim * n * (n / BOLUM_BASINA); const bSure = (n) => bBirim * n; const saat = (ms) => ms / 3_600_000; console.log(`A birim = ${aBirim.toExponential(2)} ms/kontrol, B birim = ${bBirim.toExponential(2)} ms/kalem`); console.log("kayit A suresi(saat) B suresi(saat) A pencereye sigar"); for (const kat of [1, 2, 4]) { const n = BUGUN * kat; console.log(`${String(n).padStart(9)} (x${kat}) ${saat(aSure(n)).toFixed(3).padStart(13)} ` + `${saat(bSure(n)).toFixed(5).padStart(15)} ${String(aSure(n) <= PENCERE).padStart(17)}`); } const aKapasite = (birim) => Math.sqrt((PENCERE * BOLUM_BASINA) / birim); const bKapasite = (birim) => PENCERE / birim; console.log(`\nA kapasitesi = ${(aKapasite(aBirim) / 1e6).toFixed(1)} milyon kayit ` + `(bugunun ${(aKapasite(aBirim) / BUGUN).toFixed(2)} kati)`); console.log(`B kapasitesi = ${(bKapasite(bBirim) / 1e9).toFixed(0)} milyar kayit ` + `(bugunun ${(bKapasite(bBirim) / BUGUN).toFixed(0)} kati)`); console.log(`gerceklestirim 100 kat hizlansa: A kapasitesi x${(aKapasite(aBirim / 100) / aKapasite(aBirim)).toFixed(0)}, ` + `B kapasitesi x${(bKapasite(bBirim / 100) / bKapasite(bBirim)).toFixed(0)}`); const yarim = aBirim * BUGUN * (BUGUN / (BOLUM_BASINA / 2)); console.log(`varsayim 100 yerine 50 olsa: A suresi ${saat(yarim).toFixed(3)} saat ` + `(x${(yarim / aSure(BUGUN)).toFixed(0)}), B suresi degismez`);
taranan kayit = 12000000, gunluk bolum = 4000 (= satici), 30 gunde bolum = 120000 girdi: bu makinedeki bir kosumun olcumu (A 471.7 ms, B 2.9 ms, n = 160000); asagidaki butun sureler ve kapasiteler bu iki sayiya baglidir A birim = 1.84e-6 ms/kontrol, B birim = 1.81e-5 ms/kalem kayit A suresi(saat) B suresi(saat) A pencereye sigar 12000000 (x1) 0.737 0.00006 true 24000000 (x2) 2.948 0.00012 true 48000000 (x4) 11.793 0.00024 false A kapasitesi = 28.0 milyon kayit (bugunun 2.33 kati) B kapasitesi = 794 milyar kayit (bugunun 66207 kati) gerceklestirim 100 kat hizlansa: A kapasitesi x10, B kapasitesi x100 varsayim 100 yerine 50 olsa: A suresi 1.474 saat (x2), B suresi degismez
İlk satır bölüm sayımının V3 ile tuttuğunu gösteriyor: günde 4000 bölüm, yani 4000 satıcı, otuz günde 120 000 bölüm. Bölüm sayısı satıcı sayısıyla karıştırılmamalıdır — biri günlük, öteki dönemlik bir sayıdır ve A’nın maliyeti dönemlik olana bağlıdır.
İkinci satır bütün hesabın neye dayandığını yazıyor: girdi tek bir koşumun ölçümüdür. Aşağıdaki saat ve kapasite değerlerinin hepsi o iki sayıyla birlikte değişir; sabit olan, aralarındaki oranlar ve eğrilerin biçimidir.
Üçüncü satır dersin en önemli sonucudur: A bugünün eşiğini karşılıyor. 12 milyon kaydı 0,737 saatte, yani kırk dört dakikada tarıyor; dört saatlik pencerenin içindedir. Bir kabul sınaması bu gerçekleştirimi geçirir, bir başarım raporu onu kusursuz gösterir.
Hacim iki katına çıktığında A 2,948 saate çıkar — hâlâ pencerenin içinde, ama payı dört kat küçülmüştür. Dört katına çıktığında 11,793 saattir ve pencere kırılır. Hacim iki katına çıkarken sürenin dört katına çıkması ölçeklenebilirlik sorununun tanımıdır; başarım sorunu olsaydı süre de iki katına çıkardı.
Kapasite satırları aynı şeyi tek sayıya indiriyor. A bu ölçümle 28,0 milyon kayıt taşır, bugünün 2,33 katı; başka bir makinede sayı değişir, bugünün küçük bir katı olması değişmez. B 794 milyar taşır — bu sayı aritmetik bir uzatmadır ve o hacme çok önce bellek sınırı girer; anlamı “pratikte bu iş artık zaman sınırına takılmaz”dır. İki gerçekleştirim arasındaki karar, ölçülen süreler değil bu paylardır.
Sabit Çarpan Mertebeyi Yenmez
Son iki satır iki ayrı iyileştirme türünü ayırıyor.
Gerçekleştirim 100 kat hızlandırılırsa — daha ucuz karşılaştırma, daha sıkı döngü, daha iyi veri düzeni — A’nın kapasitesi yalnız 10 katına çıkar. Süre yükün karesiyle büyüdüğü için kapasite hızın karekökü kadar büyür. Aynı 100 katlık kazanç B’nin kapasitesini 100 katına çıkarır. Sabit çarpan kazancı, mertebesi kötü olan bir çözümde kendi kadar geri dönmez.
Son satır varsayımın duyarlılığını veriyor. Fatura satırına toplanan kalem 100 yerine 50 olsa bölüm sayısı iki katına çıkar; A’nın süresi 1,474 saate, yani iki katına çıkar, B’nin süresi hiç değişmez. Varsayım yalnız bir gerçekleştirimi ilgilendiriyor. Bir varsayımın yanlış olması bir tasarımı bozarken ötekini etkilemiyorsa, bu iki tasarım arasındaki gerçek farktır.
Özet
- Başarım belirli bir yükte ölçülen tek bir değerdir; ölçeklenebilirlik ölçütün yükle değişimidir ve tek bir ölçümden çıkarılamaz.
- Ölçümde B’nin kalem başına süresi sekiz kat yükte sabit kaldı (0,02 mikrosaniye), A’nın süresi
büyüdü; türetilen
mertebesütunu son iki ikiye katlamada 1,92 ve 1,95 verdi, yani süre kalem sayısının karesiyle büyüyor. Ham süreler makineye bağlıdır, mertebe değildir. - A ile B arasındaki oran yükün işlevidir: en küçük yükte 10 dolayında, en büyük yükte yüzü aşıyor. Yüksüz bildirilen bir oran anlamsızdır.
- A bugünün eşiğini karşılıyor: 12 milyon kaydı bu ölçümle 0,737 saatte tarıyor. Hacim dört katına çıktığında 11,793 saate çıkar ve dört saatlik pencere kırılır.
- Kapasite A için bu ölçümle 28,0 milyon kayıt, bugünün 2,33 katıdır; B için zaman sınırı pratikte bağlayıcı değildir ve yerini bellek sınırı alır.
- Gerçekleştirim 100 kat hızlanırsa A’nın kapasitesi 10, B’nin kapasitesi 100 katına çıkar; sabit çarpan kazancı mertebeyi yenmez.
Sonraki Adım
Bu ders yükü değiştirdi ve kaynağı sabit tuttu: ölçümlerin hepsi tek bir süreçte, tek bir işçiyle alındı. Kapasite hesabı da bu varsayımla geçerlidir — Kabaca Büyüklük Hesabı dersinin kayıt başına 1,20 milisaniyelik payı da öyleydi. Kaynak değiştirilebilir bir büyüklüktür ve değiştirilmesinin iki yolu vardır: aynı birimi büyütmek ya da birim sayısını artırmak. İkisi farklı sınırlara çarpar, farklı koşullar ister ve maliyetleri farklı eğrilerle büyür. Sonraki ders işçi sayısını 1’den 16’ya çıkararak gün sonu işinin hızlanmasını ölçer, hızlanmanın durduğu noktayı bulur ve iki yolun maliyetini aynı eşiğin altında karşılaştırır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.