İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat