Ders 05 / 27
Çalışma Zamanı Başarımı
Ana iş parçacığındaki uzun görevlerin toplam engelleme süresiyle ölçülmesi, işin bölüntülenmesinin ölçülen etkisi, yerleşim sarsıntısının satır sayısıyla ölçeklenmesi ve görünmeyeni çizmemenin yolları.
İçindekiler
Ağ tarafı düzenlendiğinde baytlar zamanında gelir ve tekrar ziyaret neredeyse istek yapmaz. Uygulama yine de ağır hissettirebilir: istasyon listesi kaydırılırken takılıyor, süzgeç kutusuna yazılan harf ekranda geç görünüyor, tarih aralığı değiştirildiğinde sayfa bir an donuyor olabilir.
Bu sorunların hiçbiri ağda değildir. Hepsi indirilmiş kodun tek bir sırada çalışmasından doğar. Tarayıcı ve Web Platformu kursundaki Başarım Kaydı dersi bu sırayı görünür kılan aracı ele almıştı; bu ders sıranın nasıl boşaltılacağını ele alır.
Uzun Görev ve Toplam Engelleme Süresi
Ana iş parçacığı bir görev sırasıdır. Bir görev başladıktan sonra bölünemez: sürdüğü sürece kullanıcı girdisi işlenemez, kare üretilemez. Elli milisaniyeyi aşan görevler uzun görev sayılır ve eşiğin gerekçesi girdi yanıtıdır.
Ölçüt, uzun görevlerin sayısı değil, eşiği aşan bölümlerinin toplamıdır.
// engelleme.mjs — toplam engelleme suresi ve ayni isin bolunmus hali const UZUN_GOREV = 50; // ms const gorevler = [ { ad: "belge ayristirma", sure: 42 }, { ad: "paket degerlendirme", sure: 186 }, { ad: "istasyon listesi olusturma", sure: 128 }, { ad: "olcum bicimleme", sure: 61 }, { ad: "girdi isleyicisi", sure: 18 }, { ad: "kaydirma isleyicisi", sure: 33 }, ]; console.log("gorev".padEnd(28) + "sure".padStart(8) + "engelleyen".padStart(12)); let toplamEngelleme = 0; for (const g of gorevler) { const engelleyen = Math.max(0, g.sure - UZUN_GOREV); toplamEngelleme += engelleyen; console.log(g.ad.padEnd(28) + `${g.sure} ms`.padStart(8) + `${engelleyen} ms`.padStart(12)); } console.log("-".repeat(48)); console.log("toplam engelleme suresi".padEnd(28) + `${toplamEngelleme} ms`.padStart(20)); // Ayni is, 50 ms'yi asmayan parcalara bolunurse engelleme sifira iner. const bolunmus = gorevler.flatMap((g) => { const parca = Math.ceil(g.sure / UZUN_GOREV); return Array.from({ length: parca }, () => g.sure / parca); }); const bolunmusEngelleme = bolunmus.reduce((t, s) => t + Math.max(0, s - UZUN_GOREV), 0); console.log(`ayni is ${bolunmus.length} parcaya bolunurse: ${bolunmusEngelleme} ms engelleme`); console.log(`toplam is suresi degismedi: ${gorevler.reduce((t, g) => t + g.sure, 0)} ms`);
gorev sure engelleyen belge ayristirma 42 ms 0 ms paket degerlendirme 186 ms 136 ms istasyon listesi olusturma 128 ms 78 ms olcum bicimleme 61 ms 11 ms girdi isleyicisi 18 ms 0 ms kaydirma isleyicisi 33 ms 0 ms ------------------------------------------------ toplam engelleme suresi 225 ms ayni is 12 parcaya bolunurse: 0 ms engelleme toplam is suresi degismedi: 468 ms
Görev süreleri hesabın girdisidir; hesaplanan şey ölçütün kendisidir. Dört yüz altmış sekiz milisaniyelik işin iki yüz yirmi beş milisaniyesi engelleyicidir. Kırk iki milisaniyelik ayrıştırma hiç sayılmaz, çünkü eşiğin altındadır — ölçüt işin tamamını değil, kullanıcının yanıt bekleyeceği fazlalığı ölçer.
Son iki satır kritik olanı söylüyor: aynı iş elli milisaniyeyi aşmayan parçalara bölündüğünde engelleme sıfıra iner ve toplam iş süresi hiç değişmez. Yapılan şey işi azaltmak değil, araya çıkış noktaları koymaktır.
Bölüntülemenin Ölçülmesi
Modelin söylediği şey ölçülebilir. Aşağıdaki program aynı işi üç yazımla yapar ve her birinde olay döngüsünün ne kadar süreyle bloke olduğunu bir yoklayıcıyla kaydeder.
// boluntule.mjs — ayni isin tek parcada ve boluntulenmis olarak olculmesi // Yanit yoklayicisi: her turda kendini yeniden kurar, gecikmesini kaydeder. function yoklayiciBaslat(kayit) { let calisiyor = true; let onceki = performance.now(); const tur = () => { if (!calisiyor) return; const simdi = performance.now(); kayit.push(simdi - onceki); onceki = simdi; setTimeout(tur, 0); }; setTimeout(tur, 0); return () => { calisiyor = false; }; } const SATIR = 300_000; function satirMetni(i) { // Her satir icin bir miktar is: bicimlenmis bir olcum satiri uretmek. const sicaklik = -12 + ((i * 37) % 240) / 10; const zaman = new Date(Date.UTC(2026, 0, 1, 0, 0, i % 60)).toISOString(); return `kuzey-yamac-${String(i).padStart(5, "0")} ${zaman} ${sicaklik.toFixed(1)} C`.length; } async function tekParcada() { let toplam = 0; for (let i = 0; i < SATIR; i++) toplam += satirMetni(i); return toplam; } async function boluntulenmis(parcaBoyu) { let toplam = 0; for (let i = 0; i < SATIR; i++) { toplam += satirMetni(i); if ((i + 1) % parcaBoyu === 0) await new Promise((coz) => setImmediate(coz)); } return toplam; } async function olc(ad, is) { const gecikmeler = []; const durdur = yoklayiciBaslat(gecikmeler); await new Promise((coz) => setTimeout(coz, 20)); // yoklayici isinsin gecikmeler.length = 0; const bas = performance.now(); const sonuc = await is(); const gecen = performance.now() - bas; await new Promise((coz) => setTimeout(coz, 20)); // son yoklama kaydedilsin durdur(); const enBuyuk = Math.max(...gecikmeler); return { ad, gecen, enBuyuk, sonuc, tur: gecikmeler.length }; } const olcumler = [ await olc("tek parcada", tekParcada), await olc("2000 satirlik parcalar", () => boluntulenmis(2000)), await olc("500 satirlik parcalar", () => boluntulenmis(500)), ]; console.log("--- ayni is, farkli boluntuleme (sureler ortama baglidir) ---"); console.log("yaklasim".padEnd(24) + "toplam".padStart(10) + "en uzun engelleme".padStart(19) + "yoklama".padStart(9)); for (const o of olcumler) { console.log(o.ad.padEnd(24) + `${o.gecen.toFixed(1)} ms`.padStart(10) + `${o.enBuyuk.toFixed(1)} ms`.padStart(19) + String(o.tur).padStart(9)); } const taban = olcumler[0]; console.log("\nen uzun engellemenin tek parcaya orani:"); for (const o of olcumler) { console.log(` ${o.ad.padEnd(24)} ${(o.enBuyuk / taban.enBuyuk).toFixed(3)}`); } console.log(`butun yaklasimlar ayni sonucu uretti: ${new Set(olcumler.map((o) => o.sonuc)).size === 1}`);
--- ayni is, farkli boluntuleme (sureler ortama baglidir) --- yaklasim toplam en uzun engelleme yoklama tek parcada 126.9 ms 128.2 ms 18 2000 satirlik parcalar 128.5 ms 2.3 ms 144 500 satirlik parcalar 135.5 ms 1.9 ms 152 en uzun engellemenin tek parcaya orani: tek parcada 1.000 2000 satirlik parcalar 0.018 500 satirlik parcalar 0.015 butun yaklasimlar ayni sonucu uretti: true
Süreler makineye ve yüke bağlıdır; her çalıştırmada değişirler. Değişmeyen şey oranlardır. Toplam süre üç yazımda birbirine yakın kalır — bölüntüleme işi azaltmaz, hatta tur başına küçük bir ek yük getirir. En uzun engelleme ise iki mertebe düşer: yüz yirmi sekiz milisaniyelik tek blok, iki milisaniyenin altındaki bloklara dağılır.
Yoklama sayısı bunun nedenini gösterir. Tek parçalı yazımda iş sürerken sıraya hiç dönülmez; bölüntülenmiş yazımda her parçadan sonra dönülür ve bekleyen girdi işlenebilir.
Bu ölçüm bir tarayıcıda değil, tek iş parçacıklı bir çalışma zamanında yapılmıştır; ana iş
parçacığının davranışını doğru modeller, ama tarayıcıdaki çıkış noktası setImmediate
değildir. Tarayıcıda karşılığı, denetimi olay döngüsüne bırakan bir bekleme ya da boşta
çalışma çağrısıdır.
Bölüntülemenin bir de sınırı vardır: parça küçüldükçe tur başına ek yük büyür. Beş yüz satırlık parçalarda toplam süre iki bin satırlık parçalardan yüksektir. Doğru parça boyu “olabildiğince küçük” değil, kare bütçesine sığan en büyük parçadır.
İşi Sıradan Tümüyle Çıkarmak
Bölüntüleme işi ana sırada tutar ve araya girmeye izin verir. İkinci bir yol, işi sıradan tümüyle çıkarmaktır.
Tarayıcı ve Web Platformu kursunda tanıtılan çalışanlar ayrı bir iş parçacığında koşar ve ana sırayı hiç meşgul etmez. Uygun olduğu işler bellidir: saf hesap, ayrıştırma, sıkıştırma, büyük veri kümesinin süzülmesi ve sıralanması. Uygun olmadığı iş de bellidir — belge ağacına erişim. Ölçüm ayrıntısı sayfasında on binlerce ölçümün özetlenmesi çalışana taşınabilir; sonuç ana sıraya yalnızca çizilecek satırlar olarak döner.
Bedeli veri aktarımıdır. İleti geçişi kopyalama gerektirir ve büyük dizilerde bu kopyalama kazancı yiyebilir; aktarılabilir nesneler bunun için vardır.
Yerleşim Sarsıntısı Satır Sayısıyla Ölçeklenir
Düzen Sistemleri ve Duyarlı Tasarım kursundaki Animasyon Başarımı dersi zorlanmış yerleşim hesabını tanımlamıştı: bir yazma işlemi yerleşimi geçersiz kılar, sonraki bir ölçü okuması hesabı hemen zorlar. Okuma ile yazmanın sıralanması bu hesabı her turda tekrarlar ve buna yerleşim sarsıntısı denir.
Orada sayılan şey hesap adediydi. Buradaki soru, adedin liste büyüdükçe ne olduğudur.
// sarsinti.mjs — yerlesim sarsintisinin satir sayisiyla nasil olcekledigi const KARE_BUTCESI = 1000 / 60; // ms, saniyede 60 kare varsayimiyla const HESAP_MALIYETI = 0.05; // ms: tek bir yerlesim hesabinin varsayilan maliyeti const GORUNUR_SATIR = 20; // gorunum alanina sigan satir sayisi // Uc yaklasim: her biri N satir icin kac yerlesim hesabi zorlar? const yaklasimlar = { "dongu icinde oku-yaz": (n) => Math.max(0, n - 1), "okumalar sonra yazmalar": () => 0, "yalniz gorunur satirlar": (n) => Math.max(0, Math.min(n, GORUNUR_SATIR) - 1), }; console.log("satir".padStart(7) + Object.keys(yaklasimlar).map((a) => a.padStart(26)).join("")); for (const n of [10, 50, 200, 1000, 5000]) { const hucreler = Object.values(yaklasimlar).map((f) => { const hesap = f(n); const sure = hesap * HESAP_MALIYETI; return `${hesap} hesap / ${sure.toFixed(1)} ms`.padStart(26); }); console.log(String(n).padStart(7) + hucreler.join("")); } console.log(`\nkare butcesi: ${KARE_BUTCESI.toFixed(2)} ms`); console.log("kac kare butcesi tuketilir:"); console.log("satir".padStart(7) + Object.keys(yaklasimlar).map((a) => a.padStart(26)).join("")); for (const n of [10, 50, 200, 1000, 5000]) { const hucreler = Object.values(yaklasimlar).map((f) => (f(n) * HESAP_MALIYETI / KARE_BUTCESI).toFixed(1).padStart(26)); console.log(String(n).padStart(7) + hucreler.join("")); } console.log("\nhesap maliyeti bir varsayimdir; degistiginde sutunlarin orani degismez.");
satir dongu icinde oku-yaz okumalar sonra yazmalar yalniz gorunur satirlar
10 9 hesap / 0.5 ms 0 hesap / 0.0 ms 9 hesap / 0.5 ms
50 49 hesap / 2.5 ms 0 hesap / 0.0 ms 19 hesap / 1.0 ms
200 199 hesap / 10.0 ms 0 hesap / 0.0 ms 19 hesap / 1.0 ms
1000 999 hesap / 50.0 ms 0 hesap / 0.0 ms 19 hesap / 1.0 ms
5000 4999 hesap / 250.0 ms 0 hesap / 0.0 ms 19 hesap / 1.0 ms
kare butcesi: 16.67 ms
kac kare butcesi tuketilir:
satir dongu icinde oku-yaz okumalar sonra yazmalar yalniz gorunur satirlar
10 0.0 0.0 0.0
50 0.1 0.0 0.1
200 0.6 0.0 0.1
1000 3.0 0.0 0.1
5000 15.0 0.0 0.1
hesap maliyeti bir varsayimdir; degistiginde sutunlarin orani degismez.
On satırlık bir listede üç yaklaşım arasında fark yoktur; sorun bu yüzden geliştirme sırasında görünmez. Beş bin satırda ilk yaklaşım on beş kare bütçesi tüketir, yani çeyrek saniye boyunca hiçbir kare üretilmez.
İki çözümün karakteri farklıdır. Okumaları ve yazmaları öbeklemek maliyeti sıfıra indirir ve liste büyüklüğünden bağımsızdır; yalnızca görünür satırları işlemek maliyeti sabitler ve liste ne kadar büyürse büyüsün değişmez. İkincisi, sarsıntıyı olduğu gibi bırakır ama ölçeklenmesini keser.
Görünmeyeni Çizmemek
Görünür olanı işlemek fikri yerleşim sarsıntısıyla sınırlı değildir; tarayıcının çizim hattının tamamı için geçerlidir.
Sanallaştırma listenin yalnızca görünen bölümünü belge ağacına koyar. Kaydırıldıkça düğümler yeniden kullanılır. Bedeli, ağaçta bulunmayan içeriğin sayfa içi aramada ve erişilebilirlik ağacında da bulunmamasıdır.
İçerik görünürlüğü bildirimi aynı hedefe işaretlemeyi bozmadan yaklaşır: content-visibility
özelliği bir kutunun ekran dışındayken çizim ve yerleşim işlerinin atlanmasını sağlar.
Atlanan işin kutusu yine de bir yer kaplamalıdır; yoksa kaydırma çubuğu içerik göründükçe
zıplar. Bu yüzden bildirimle birlikte tahmini bir boyut verilir.
Sınırlama bildirimi ise bir kutunun etkisini kendi sınırlarında tutar. Animasyon
Başarımı dersinde tanıtılan contain bildirimi, bir kart içindeki değişikliğin yerleşim
hesabını sayfanın tamamına yaymasını önler.
Etkileşim Gecikmesinin Üç Bileşeni
Sonraki boyaya etkileşim tek bir sayıdır ama üç ayrı gecikmenin toplamıdır ve düzeltmeleri farklıdır.
Girdi gecikmesi, kullanıcı etkileşimi ile işleyicinin başlaması arasındaki süredir. Nedeni, o sırada süren başka bir görevdir. Çözümü bölüntüleme ya da işi çalışana taşımaktır; işleyiciyi hızlandırmak bu bölüme dokunmaz.
İşleme süresi, işleyicilerin kendi süresidir. Çözümü işleyicinin küçültülmesi ve ertelenebilir işin ertelenmesidir: kullanıcının gördüğü değişikliği hemen yap, kaydı ve istatistiği sonraya bırak.
Sunum gecikmesi, işleyici bittikten sonra karenin üretilmesine kadar geçen süredir. Nedeni işleyicinin tetiklediği yerleşim ve boyama işinin büyüklüğüdür. Çözümü, güncellenen alanı küçültmektir.
Kaydın hangi bileşenin büyüdüğünü göstermesi, hangi düzeltmenin denenmeyeceğini de söyler. Ölçmeden yapılan eniyileme çoğu zaman yanlış bileşene uygulanır.
Özet
- Toplam engelleme süresi, uzun görevlerin elli milisaniyelik eşiği aşan bölümlerinin toplamıdır; işi bölüntülemek toplam süreyi değiştirmeden bu ölçütü sıfıra indirebilir.
- Ölçüm bunu doğrular: aynı iş bölüntülendiğinde en uzun engelleme iki mertebe düşer, toplam süre yakın kalır; parça küçüldükçe tur başına ek yük artar.
- Çalışanlar işi ana sıradan tümüyle çıkarır; sınırları belge ağacına erişememek ve veri kopyalama maliyetidir.
- Yerleşim sarsıntısının maliyeti satır sayısıyla doğrusal büyür; öbekleme maliyeti sıfıra indirir, görünür satırlarla çalışmak sabitler.
- Sonraki boyaya etkileşim girdi gecikmesi, işleme süresi ve sunum gecikmesinin toplamıdır ve her bileşenin düzeltmesi ayrıdır.
Sonraki Adım
Beş derste ölçütler tanımlandı, kritik yol kısaltıldı, varlıklar boyutlandırıldı, ağ katmanı ayarlandı ve çalışma zamanı boşaltıldı. Bütün bunların ortak açığı şudur: hepsi bir kez yapılmış iyileştirmelerdir. Yeni bir özellik eklendiğinde paket büyür, yeni bir görsel konduğunda ilk ekranın ağırlığı artar, bir kütüphane ortak parçaya girdiğinde tekrar ziyaret kazancı erir. Bu erimenin bir sonraki yayında fark edilmesi gerekir, aylar sonra şikâyetle değil. Sonraki ders ölçütleri yazılı bir bütçeye bağlar ve iki derleme çıktısını karşılaştırarak gerilemeyi yakalayan bir denetleyici kurar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.