deniz.in

Piyasalar

Hava durumu

Hava durumu yükleniyor

· kaynak Hacker News – Front Page (native)

Derinlemesine bir inceleme, tarayıcının main thread'ini frontend performansının en maliyetli kaynağı olarak çerçeveliyor

Hacker News'in ön sayfasına ulaşan teknik bir yazı, etkileşimli ekranları donduran şeyin bundle boyutu ya da önbellekleme değil, main thread'in bloke edilmesi olduğunu savunuyor ve bu thread'i akıllıca kullanma taktiklerini sıralıyor.

Derinlemesine bir inceleme, tarayıcının main thread'ini frontend performansının en maliyetli kaynağı olarak çerçeveliyor

kciter.so'da yayımlanan ve şu anda Hacker News'in ön sayfasında yer alan "The Browser's Main Thread Is Expensive" başlıklı uzun bir mühendislik yazısı, frontend performans çalışmalarının, bir sayfa gerçekten etkileşimli hale geldiğinde en çok önem taşıyan kaynağı sistematik olarak gözden kaçırdığını savunuyor.

Yazarın açılış gözlemi, çoğu optimizasyon sohbetinin bundle'ları küçültme, ağ isteklerini azaltma ve önbellek ayarlama etrafında dönmesidir. Yazıya göre bu teknikler fena değil, ama ekranı gerçekten donduran mekanizmayı gözden kaçırıyorlar: main thread bloke edildiğinde, ne kadar ağ veya bundle tasarrufu yaparsanız yapın bu işe yaramaz. Belirtiler tanıdık ve sinsice — ara ara takılan bir kaydırma, yarım nefes geç yanıt veren bir buton, yazdığınız karakterlerin tuş vuruşlarınızın hafif arkasından gelmesi.

Tek thread, iki iş

Yazıya göre tarayıcı çok sayıda thread çalıştırıyor, ama geliştirici kodundan erişilebilen neredeyse her şey tek bir thread üzerinde yoğunlaşmış durumda: main thread. Bu thread iki geniş kategorideki işi yürütüyor.

İlki JavaScript çalıştırmak — uygulama kodu, event handler'lar, timer'lar, ağ yanıtı callback'leri ve framework iç yapıları, hepsi ekranın yenileme döngüsüyle hiçbir ilişkisi olmadan kuyruk sırasına göre işleniyor. İkincisi ise ekranı çizmek. DOM ya da stiller değiştiğinde, main thread requestAnimationFrame callback'lerini, stil hesaplamasını, layout'u (reflow olarak da adlandırılır) ve paint'i sırayla yürütüyor. Yalnızca son kompozitleme adımı ayrı bir compositor thread'ine devrediliyor; bu da render pipeline'ının ön yarısının büyük bölümünün main thread'e ait olduğu anlamına geliyor.

Bu ayrım, yazının canlı bir demo ile gösterdiği bir detayı açıklıyor: JavaScript main thread'i ele geçirdiğinde, JS ile sürülen bir animasyon ve bir metin girişi ikisi de donuyor, ama transform kullanan bir CSS animasyonu dönmeye devam ediyor — çünkü o, compositor thread'inde çalışıyor.

Bütçeler ve long task'ler

Yazı bu kısıtı sayılarla ortaya koyuyor. Yaygın bir 60Hz ekranda tarayıcının her kareyi üretmek için kabaca 16,6 milisaniyesi var ve tarayıcının kendi yükü çıkarıldığında pratik bütçe genellikle 10 milisaniye civarında. 120Hz'lik bir cihazda bu bütçe yarıya iniyor.

JavaScript tek thread'li bir event loop kullandığından, 200 milisaniye çalışan bir fonksiyon thread'i bu sürenin tamamı boyunca meşgul tutuyor — ne bir repaint yapılıyor, ne de tıklamalar işleniyor. Yazı, bu tür görevlere long task dendiğini ve 50 milisaniyenin üzerindeki her şeyin genellikle sorun olarak kabul edildiğini belirtiyor.

Yazar bunu doğrudan standart web performans metrikleriyle ilişkilendiriyor: Kullanıcı eyleminden sonra ekranın ne kadar hızlı yanıt verdiğini ölçen INP (Interaction to Next Paint) ve yükleme sırasında thread'in ne kadar süre bloke edildiğini ölçen TBT (Total Blocking Time), esasen ikisi de main thread doluluğunun ifadeleri.

Temel olarak bölme

Yazı çözümleri iki aileye ayırıyor: thread'in zamanını içeriden iyi bölmek ya da işi thread'ten tamamen taşımak. İlk aile, yazarın splitting, batching, prioritizing ve deferring olarak adlandırdığı dört hamleye dayanıyor — ilk ikisi görevlerin ne kadar büyük olduğunu şekillendiriyor, son ikisi ne zaman çalışacaklarına karar veriyor. Splitting, diğer her şeyin aralarına yerleştirilebilmesi ya da ertelenebilmesi için görevlerin önce sınırlara ihtiyacı olması nedeniyle temel olarak tanımlanıyor.

Merkezi örnek olayı, canlı yayın sohbet paneli. Popüler yayınlarda sohbet saniyede yüzlerce mesaja kadar çıkabiliyor, onlarca mesajlık kümeler halinde geliyor ve bir izleyici katıldığında birikmiş yüzlerce mesaj aynı anda düşüyor. Tüm kümeyi tek bir görevde render etmek, DOM oluşturma, stil hesaplama, layout ve paint döngülerinin arka arkaya yüzlerce kez yapılması demek — bu sırada izleyicinin kendi yazma girişi takılıyor ve diğer animasyonlar seğiriyor.

Yazıya göre çözüm, kümeyi küçük parçalara bölmek ve parçalar arasında main thread'in kontrolünü bırakmak; böylece tarayıcı kuyruktaki ekran güncellemelerine ve girişe yetişebiliyor. Yazarın eşlik eden demosu, sohbet seli sırasında kare hızlarının keskin biçimde düştüğü "anında render" modu ile, mesajların yine tek tek göründüğü ama giriş yanıt hızının geri döndüğü "yield ederek render" modunu karşılaştırıyor.

Neden önemli

Yazı, yaygın bir hata ayıklama içgüdüsüne karşı yararlı bir düzeltme. Yazara göre geliştiriciler jank gördüklerinde algoritmalarını yavaşlık konusunda sorgulama eğiliminde — oysa çoğu durumda kod yavaş değildir, sadece thread'i yanlış zamanda tutmaktadır. Veri yoğun, gerçek zamanlı arayüzler inşa eden ekipler için pratik çıkarım şu: görev şekli ve yield disiplini en az ham işlem maliyeti kadar önemlidir ve INP ile TBT, çok pahalı tek bir thread'in ne kadar dikkatli harcandığının ölçümleri olarak ele alınmalıdır.

  • #frontend
  • #web-performance
  • #main-thread
  • #browser
  • #inp

İlgili yazılar