1 puan yazan GN⁺ 2024-09-08 | 1 yorum | WhatsApp'ta paylaş
  • GitHub Pages Brotli desteklemediği için, HTML’i kayıpsız WebP görüntüsü olarak kodlayıp tarayıcının görüntü çözücüsü ve JavaScript ile geri yükleyerek aktarılan veri miktarını azaltmaya yönelik bir deney
  • WASM Brotli çözücüsü 71 KiB~200 KB ek maliyet getiriyor; Compression Streams API ise yalnızca gzip, deflate, deflate-raw desteklediğinden Brotli çözmeyi dolanmak için uygun bir yol olmakta zorlanıyor
  • VP8L kayıpsız WebP; tahmin dönüşümü, 16x16 blok başına Huffman ağacı yeniden kullanımı ve color cache sayesinde metin benzeri verilerde de gzip’ten daha küçük sonuçlar üretebiliyor
  • Test HTML’i 439,478 bayttan gzip ile 94,683 bayta, WebP ile 43,182 bayta kadar düştü; Brotli’nin 37 KiB sonucundan büyük olsa da gzip’e göre yaklaşık 2,2 kat daha küçüktü
  • Canvas 2D’nin anti-fingerprinting gürültüsü, WebGL readPixels değişiklikleri, ilk baştaki boş ekran ve kaydırma konumunu geri yükleme sorunları nedeniyle gerçek uygulamadan çok tarayıcı kısıtlarını ortaya koyan bir hack niteliğinde

GitHub Pages’te Brotli kullanılamaması sorunu

  • Sayfa yükleme süresini azaltırken HTTP sıkıştırması, HTML minify’dan daha büyük etki yaratır
  • HTTP, Content-Encoding başlığıyla gzip ve Brotli destekler
    • gzip ucuzdur, bu yüzden genellikle varsayılan olarak etkinleştirilir
    • Brotli’nin sıkıştırma oranı genellikle gzip’ten iyidir ama çok daha yavaştır
  • GitHub Pages Brotli desteklemediği için, sitenin en uzun yazısı olan Recovering garbled Bitcoin addresses, Brotli ile 37 KiB olabilecekken gzip ile 92 KiB olur
  • Bu fark yüzünden yükleme süresi gereksiz yere 2,5 kat artar

İlk akla gelen alternatif ve tıkanılan noktalar

  • GitHub önceden sıkıştırılmış Brotli dosyalarının yüklenmesini ve sunulmasını destekleseydi sorun aşılabilirdi, ancak böyle bir özellik sunulmuyor
  • İstemci tarafında JavaScript ile doğrudan açma yaklaşımında WASM çözücünün boyutu nedeniyle avantaj azalır
  • Tarayıcının HTTP yığınında Brotli çözücüsü vardır, ancak Compression Streams API içindeki DecompressionStream yalnızca gzip, deflate, deflate-raw kabul eder
  • gzip Zopfli ile önceden sıkıştırılsa bile 86 KiB olur, yani Brotli’den daha büyük kalır

Görüntü biçimini sıkıştırma konteyneri gibi kullanmak

  • Tarayıcılar görüntüleri çözebildiği için, veriyi görüntü piksellerine koyup Canvas API ile tekrar okursanız yeni bir açma mantığı eklemeniz gerekmez
  • GIF, veriyi row-major sırayla açtıktan sonra LZW uygular; ancak gzip’in DEFLATE’i LZW’nin yerini almak üzere tasarlanmış bir yöntem olduğundan kazanç beklemek zordur
  • PNG DEFLATE kullanır, fakat pikselin ham hâlini değil, önce komşu piksellerle farkı sıkıştıran bir tahmin dönüşümü uygular
    • Örnek: [a, b, c, d] yerine [a, b-a, c-b, d-c] sıkıştırılır
    • Tahmin değeri ile gerçek değer arasındaki fark ne kadar küçükse Huffman sıkıştırması için o kadar avantajlıdır
  • Deneyin özü, kayıpsız WebP olan VP8L’yi genel bayt verisi sıkıştırma amacıyla kullanmaktır

VP8L’in gzip’ten farkları

  • WebP’nin kayıplı ve kayıpsız varyantları vardır; burada yalnızca kayıpsız biçim olan VP8L ele alınır
  • VP8L, PNG gibi tahmin dönüşümü kullanır, ancak DEFLATE yerine Google’ın yaptığı DEFLATE benzeri bir yöntem kullanır
  • DEFLATE dosyayı birden çok parçaya bölebilir ve her parça için uygun Huffman ağaçları kullanabilir
    • JavaScript, SVG ve markup tek bir HTML içinde karıştığında farklı ağaçlar daha avantajlı olabilir
  • VP8L keyfi büyüklükte bir Huffman ağacı tablosu tanımlar ve her 16x16 piksel bloğu için farklı bir ağaç kullanabilir
    • JavaScript’ten sonra CSS, ardından tekrar JavaScript geldiğinde DEFLATE benzer ağaçları birden fazla kez kodlayabilir
    • VP8L ağaçları yeniden kullanabildiği için ağaçları daha sık ve daha düşük maliyetle değiştirebilir
  • VP8L’in color cache özelliği, belirli niteliklere sahip yakın geçmişteki pikselleri kopyala anlamında değerleri kısa ifade edebilir

İlk WebP sıkıştırma deneyi

  • Test dosyası Recovering garbled Bitcoin addresses HTML’idir
    • Orijinal boyut: 439,478 bayt
    • gzip --best: 94,683 bayt
  • Rust’taki webp crate’i ile baytlar grayscale RGB görüntüsüne dönüştürülüp kayıpsız WebP olarak sıkıştırıldı
  • Grayscale kullanılmasının nedeni WebP’nin subtract green dönüşümüdür
    • Grayscale’de G kanalı R/B’den çıkarıldığında R/B fiilen 0 olur
    • WebP üç kanalı ayrı Huffman ağaçlarıyla kodladığı için sabit değerli kanallar O(1) alana yakın hâle gelir
  • Başta 1xN görüntü yapılmak istendi, ancak WebP en fazla 16383x16383 desteklediğinden VP8_ENC_ERROR_BAD_DIMENSION hatası oluştu
  • 16383xN biçimine ayarlanınca sonuç 45,604 bayt oldu; gzip’ten 2 kat küçük ve bzip2’nin 49,764 baytından da küçüktü

WebP’ye özel ayarlamalar

  • Geniş görüntüde row-major sıra kullanılırsa 16x16 blok içine girdide birbirinden uzak baytlar karışır
  • Görüntü biçimi dikey olarak uzun 27x16383 yapıldığında sıkıştırma sonucu 43,232 bayta düştü
  • cwebp’nin sıkıştırma performansı ayarı olan method 0~6 arasında karşılaştırıldı
    • method 0: 48,902
    • method 1: 43,546
    • method 2: 43,442
    • method 3: 43,292
    • method 4: 43,232
    • method 5: 43,182
    • method 6: 43,182
  • method 5, method 6 ile aynı boyutu üretirken daha hızlı seçenek olarak seçildi
  • Bu durumda WebP, gzip’ten 2,2 kat küçük ve Brotli’den 1,2 kat büyüktü

Birden çok dosyada benchmark

  • Karşılaştırma verileri snappy testdata, Canterbury Corpus ve Large Corpus ile 2 SVG dosyasıydı
  • Karşılaştırılan biçimler gzip --best, brotli --best, bzip2 --best ve WebP sıkıştırma script’iydi
  • WebP, çok küçük dosyalar olan grammar.lsp, xargs.1 ve bazı istisnalar dışında neredeyse her zaman gzip’ten daha iyiydi
  • İstisnalar kennedy.xls ve paper-100k.pdf idi
    • paper-100k.pdf, 19 KB XML’in ardından sıkıştırılmış veri içerdiği için fiilen küçük verinin ölçüldüğü bir durumdur
    • kennedy.xls için Brotli/bzip2 göreli performansı da gariptir; yakın konumlarda çok sayıda heterojen veri bulunduğundan sıkıştırıcıların işlemesi zor bir dosya olabilir
  • WebP genel olarak bzip2’den biraz daha kötüydü, ancak bazı durumlarda onu geçti
  • WebP, fireworks.jpeg gibi neredeyse tekdüze rastgele blob’a yakın istisnalar dışında her zaman Brotli’den kötüydü
  • Büyük plain-text verilerde gzip’e kıyasla ölçülebilir iyileşme sağladı
    • SVG dosyalarında da iyileşme vardı
    • html_x_4 içinde WebP %3,3 sıkıştırma oranı kaydetti; Brotli’nin %2,8 sonucundan kötü olsa da gzip’in %13 sonucundan belirgin biçimde iyiydi

JavaScript ile geri yükleme

  • WebP çözmenin kendisi fetch, createImageBitmap, OffscreenCanvas, getImageData, TextDecoder ile uygulanabilir
  • Pikselin R kanalı orijinal HTML baytları olarak kullanılır; bunlar UTF-8 olarak çözüldükten sonra document.documentElement.innerHTML içine yerleştirilir
  • Canvas API fingerprinting için sık kullanıldığından bazı tarayıcılar getImageData sonucuna gürültü ekler
    • Firefox’un strict tracking protection modunda piksellerin %1’den azı etkilenebilir
    • HTML’de bu gürültü yazım hataları gibi görünür
  • WebGL’in readPixels kullanılması o dönemde gürültüsüz çalışıyordu
    • WebGL dokuları güvenilir biçimde yalnızca 2048x2048 boyutuna kadar desteklediğinden boyut sınırını yeniden ayarlamak gerekir
    • Bu geri yükleme kodu minify sonrası yaklaşık 550 bayttı
  • WebP ve kod birlikte 44 KiB oldu; gzip 92 KiB, Brotli 37 KiB ile karşılaştırıldı
  • 15 Nisan 2026’dan sonra Firefox readPixels için de anti-fingerprinting eklediğinden, bu yöntem artık aynı hâliyle çalışmıyor

Ekran titremesi ve kaydırma sorunu

  • await promise tabanlı işlendiği için, WebP indirmesi bitmeden tarayıcı script çalışmasının bittiğine karar verir
  • DOM hâlâ boş olduğundan kullanıcı kısa süreliğine boş beyaz ekran görür
  • Hafifletme olarak stil ve sayfanın üst kısmındaki yaklaşık 8 KiB gzip HTML içinde bırakılıp yalnızca viewport altındaki içerik WebP ile sıkıştırılabilir
  • Yenileme sırasında kaydırma konumunun geri yüklenmesi de sorun olur
    • Örneğin Y = 5000px konumundayken sayfa yenilendiğinde sayfa yüksekliği 0px ise konum sıfırlanır
    • Çok büyük geçici bir div eklemek yardımcı olabilir
  • Geçerli belgeyi yeni belgeyle değiştirmeden güncelleyebilmek için document.write yerine document.documentElement.innerHTML ataması yapılmalıdır

WebP’yi doğrudan JavaScript içine koymak

  • Gecikmeyi biraz daha azaltmak için WebP JavaScript’in içine doğrudan gömülebilir
  • En basit yöntem base64 data URL’dir
  • base64 orijinal boyutu 1,33 kat artırır, ancak gzip bu artışın neredeyse tamamını dengeler
    • compressed.webp base64 yapılınca 57,576 bayt olur
    • Bu, gzip --best ile sıkıştırılınca 43,519 bayta iner
  • WebP gibi sıkıştırılmış blob’lar neredeyse tekdüze rastgele veridir; base64’ün 8-bit’ten 6-bit’e dönüşüm sonucunda gzip’in Huffman ağacı fiilen ters dönüşüm gibi çalışır
  • Unicode ve UTF-16 da kullanılabilir, ancak base64 yeterli bir ilk çözüm olarak kalır

Gerçek uygulama ve sonraki durum

  • Yazının yazıldığı sırada bu sayfanın kendisi, eski tarayıcılar veya JavaScript’in kapalı olduğu ortamlar dışında “Fool me twice” bölümünden itibaren WebP ile sıkıştırılmıştı
  • Sayfanın WebP görüntüsü gerçek kodda dikey olarak uzun ve dardı, ancak güzel görünmesi için kare bir WebP örneği de sağlanmıştı
  • Görüntüdeki parlak üst ve alt kısımlar metin ve koddu; yaklaşık 1/5 noktasındaki taralı alan diyagram, karanlık alanların çoğu ise diyagram içindeki metindi
  • Gerçek tasarruf sınırlıydı
    • Önceki gzip sayfası: 88 KiB
    • WebP uygulandıktan sonra gzip sayfası: 83 KiB
    • Brotli tahmini: 69 KiB
  • 15 Nisan 2026’dan sonra Firefox ziyaretçilerine bozuk içerik görünmemesi için şu anda sıkıştırılmamış sayfaya geçildi
  • Rust kodu, corpus ve diğer dosyalar GitHub üzerinde yayımlandı

1 yorum

 
GN⁺ 2024-09-08
Hacker News yorumları
  • Gecikmeyi yok sayarsanız öyle olabilir, ama pratikte yükleme süresini ancak %0,001 civarında artırıyor gibi görünüyor.
    Boyuttaki artış, gidiş-dönüş gecikmesine kıyasla anlamlı değil; 55KiB daha az aktararak kazanılan zamandan daha fazlası sıkıştırmayı açmaya harcanabilir.
    İlginç bir deney olsa da bu durumda kullanıcı deneyiminin daha da kötüleşmesi muhtemel; hız neredeyse aynı kalırken yalnızca uyumluluk düşecek gibi.

    • Yalnızca yükleme süresini optimize edip herkesin veri hızını varsayıyorsanız doğru, ama çoğu zaman bir web sitesi ya da uygulama geliştiricisinin benim adıma hız/veri ödünleşimi kararını bu kadar kolay vermesini istemiyorum.
      Sorun şu: TFA yazarı gibi 100KB’ı 50KB’a indirmeye uğraşanlar da var, ama ben dolaşım verisiyle yalnızca bir restoranın çalışma saatlerine bakmak isterken hiç çekinmeden onlarca MB görüntü gönderen yerler de var.
      Kaynak bilinci var, fakat ne yazık ki çok dengesiz dağılmış durumda.
    • Mesele yalnızca sıkıştırma açma süresi değil. Her şeyi indirdikten sonra sıkıştırmayı açabilirsiniz, oysa tarayıcı sunucudan akış hâlinde gelen HTML’i anında açıp render edebilir.
      Bağlantı koparsa her şeyi kaybedersiniz; indirilen kısmı bile okumanız imkânsız hâle gelir.
      Normal bağlantılarda fark anlamsızdır; 50KB’ın önemli olacağı kadar çok yavaş ya da kararsız bağlantılarda ise bu yöntem kesinlikle daha kötü olur. İlginç bir deney, ama sitelerde uygulanmasa iyi olur.
    • Önbellekte yoksa 850K’lık Symbols-2048-em%20Nerd%20Font%20Complete.woff2 dosyası var; bu da farkı neredeyse tamamen kapatıyor.
    • Bu kadar boyut farkı, gereken gidiş-dönüş sayısını etkileyecek kadar büyük. Makul, modern bir başlangıç tıkanıklık penceresi değeriyle yaklaşık bir gidiş-dönüş azalması gerekir.
      2,5 kat fark olmayabilir, ama %0,001 de değil.
    • Bir TCP alım penceresinden daha az tasarruf ediliyorsa gecikmede fark yaratmaz.
      Kayıplı ağlarda fark olabilir, ama emin değilim.
  • readPixels’ın neden parmak izi önleme kapsamında olmadığını bilmiyorum. Sayfanın tamamına neredeyse görünmez yazım hataları serpiştirilmeyeceği için benim açımdan sorun yok.
    gzip’lenmiş HTML’de yalnızca stiller ve sayfanın üst kısmındaki yaklaşık 8KiB bırakılıp, görüntü alanının altındaki içeriğin WebP ile sıkıştırıldığı kısmı görünce metnin neden rastgele bir cümleden sonra birden kesilip ardından boş sayfa geldiği açıklığa kavuştu.
    LibreWolf kullandığım için WebGL kapalı; WebGL gerektiren rastgele web oyunlarını Chromium ile açıyorum. WebGL’i açınca yazı düzgün çalıştı ve dürüst olmak gerekirse oldukça temiz bir teknik.

    • Tüm modern web tarayıcılarında, hatta parmak izi koruması açıkken bile çalışmıyor ve eski tarayıcılar için geri dönüş yolu da yoksa buna temiz demek zor.
      WWW, sıradan HTML ile başlayıp kademeli olarak geliştirilerek evrensel erişilebilir olmalı.
  • Web tarayıcılarında Brotli’yi doğrudan kullanmak da mümkün, ama elbette kısıtları var.
    2022 JS1024 başvurusu [1] bence bu kavramın ilk gösterimiydi; keyfî sıkıştırma için bir kavram kanıtı kodu da var. Ne yazık ki asıl amaç olan boyut kodlaması için uygun değildi.
    Temel kısıt, fiilen ASCII karakterlerle sınırlı olması ve bariz nedenlerle render yığınına çok hassas olması. Şu anda Firefox’ta çalışmıyor gibi görünüyor.

    [1] https://js1024.fun/demos/2022/18/readme

    [2] https://gist.github.com/lifthrasiir/1c7f9c5a421ad39c1af19a9c...

    • Bu yaklaşımı anlamanın anahtarı, bunun gerçekten nasıl sıkıştırılıp yerleştirildiğinin ayrıntılarına inmeden de anlaşılabilen şu kısım:
      Yalnızca Brotli’nin başlangıçta tasarlandığı WOFF2 yazı tipi dosya biçimini kullanmak mümkün; fakat bundan yararlanmak için eksiksiz bir yazı tipi dosyası oluşturmak gerekiyor.
      Güncel tarayıcılar, güvenilmeyen yazı tipi dosyalarını doğrudan sisteme koymak çok tehlikeli olduğundan genellikle yazı tiplerini OpenType Sanitizer (OTS) ile temizliyor; bu yüzden hem OTS’nin kabul edeceği kadar düzgün hem de istenen bayt dizisini içinde taşıyıp çıkarılabilir kılan bir WOFF2 dosyası üretmek gerekiyor.
      Birçok başarısız denemeden sonra, neredeyse kısıtsız biçimde 2 baytlık işaretli tamsayı dizileri olarak kodlanan glif genişliklerine, yani advance değerlerine karar verilmesi fikri harika.
    • Düzeltme: Firefox’ta da hâlâ çalışıyor. Sadece Firefox’ta yakınlaştırma/uzaklaştırma oranının tam olarak %100 olması gerektiğini unutmuşum.
    • Bu teknik gerçekten şaşırtıcı ve benim yazımdan çok daha havalı. Takdir ediyorum.
  • Chromium tarafı uzun süre engelliyordu, ama zstd de artık web’e giriyor. Sonunda Chrome’a girdi; artık sadece Safari’nin yetişmesi kaldı.

    • Her şeyi Zstandard’a geçirmek isterim, ama bildiğim kadarıyla bu özel durumda sıkıştırma açıcı bellek kullanımı aynıyken Brotli ile Zstandard neredeyse benzer.
    • En azından yapılacaklar listesinde gibi görünüyor: https://webkit.org/standards-positions/#position-168
  • Batch Compress'i (https://batchcompress.com/en) geliştiriyorum ve kısa süre önce WebP desteği ekledikten çok geçmeden varsayılan olarak değiştirdim
    Bildiğim kadarıyla zaten web sıkıştırma araçları arasında en küçük JPEG'leri üretiyorduk; WebP ise JPEG'in yaklaşık %50'si boyutunda çıkıyordu. Desteği ekledikten kısa süre sonra varsayılanı değiştirmek kolay bir karardı
    Sitenin epey kullanıcısı olduğu için WebP'yi varsayılan yaptıktan sonra biraz şikâyet gelir diye düşünmüştüm, ama aradan yaklaşık bir ay geçmişken WebP ile ilgili yalnızca bir soru ya da şikâyet geldi
    Artık neredeyse tüm araçlar ve tarayıcılar WebP'yi destekliyor gibi. Yakın zamanda WebP görsel yüklemelerini düzgün işleyemediği için sonraki adımı engelleyen yalnızca bir web sitesi gördüm; bugünlerde neredeyse her yerde iyi destekleniyor

    • WebP, JPEG'e kıyasla dosya boyutunu %15~20'den fazla azaltıyorsa, bu tasarruf sıkıştırma iyileştirmesinden değil kalite düşüşünden kaynaklanıyordur
      JPEG iyi sıkıştırılıp optimize edilirse WebP'nin çok gerisinde kalmamalı
      JPEG'den neredeyse farksız görünen bir WebP üretip dosya boyutunu her zaman küçültebilirsiniz; ama neredeyse aynı görünen bir JPEG'e yeniden sıkıştırınca da aynı şey olur
      Bu, tüm kayıplı sıkıştırma codec'lerinin özelliği; kalite yükseldikçe dosya boyutu üstel olarak büyüdüğü için insanlar, neredeyse görünmez çok küçük bir kalite düşüşünün bile dosya boyutunu ne kadar değiştirebildiğine hep şaşırıyor
    • WebP'nin JPEG'in yaklaşık %50'si boyutunda olduğu söylenirken bunun hangi kalite karşılaştırma metriğine göre olduğunu merak ediyorum
      WebP, karanlık alanlardaki ayrıntıları mahveden berbat varsayılanlarıyla kötü şöhretliydi
  • Kaynağa bakarken doctype bildiriminde bir boşluğun eksik olduğunu gördüm. Mevcut biçim hatalı; arada boşluk olmalı

  • Bu numarayı daha önce denemiştim. Tuhaf şekilde ne için kullandığımı hatırlamıyorum; muhtemelen mümkün olup olmadığını görmek içindi ve buraya da yorum bırakmıştım: https://gist.github.com/gasman/2560551?permalink_comment_id=...
    Eski bir prototip de buldum; sanırım sadece testti: https://retr0.id/stuff/bee_movie.webp.html

    • O sayfa benim fare hareketleri uzantımı bozdu
      Artık eklenti değil uzantı denen, betik enjekte etmeye benzer şeyler bunlar; neyse, başlangıçta “çöp”ü iletip arkasına JS ekleyerek sayfaya geri döndürme yaklaşımı ilginç
      İçimdeki güvenlik meraklısı, yorum formu gibi kullanıcı tarafından sağlanan veriler olduğunda bunun saldırıya açık hale gelip gelmeyeceğini merak ediyor
      Birisi yoruma koyulacak bayt dizisini bulup, sıkıştırmadan sonra benim betiğimden önce yer alarak çalıştırılan bir script etiketine dönüşmesini sağlayabilir mi diye düşünüyorum
    • Deneyimlerime göre WebP, bu tekniğin gerçekten yararlı olduğu genel duruma, yani 10 KB altı verilere pek uygun değildi
      WebP kayıpsızın PNG'ye kattıklarının çoğu kodlamadan çok modellemeyle ilgili; bu tür metin sıkıştırma ise WebP'nin yalnızca kodlama kısmını kullanıyor
  • Google Fonts'u çıkarmak da sayfa yüklenme süresini biraz iyileştirir. Çünkü uzak bir sunucudan yükleniyor ve ek el sıkışma gerektiriyor

    • Ama yeterince çok başka site de o yazı tipini kullanıyorsa zaten yerelde bulunuyor olabilir
  • Bu sayfa en azından Sailfish OS tarayıcısında bozuluyor. Şu paragraftan sonra uzun bir boş alan var
    “Alright, so we’re dealing with 92 KiB for gzip vs 37 + 71 KiB for Brotli. Umm…”
    Yine de gzip ve Brotli HTML sıkıştırmasının ek yükü, günümüz web sitelerinin kullandığı JS, görsel ve video miktarına kıyasla hiçbir şey

    • Orion, Safari ve LibreWolf'ta da aynı. Bu Chrome'a özel bir sayfa mı?
    • Mull'da da aynı
  • Şahsen bu formatı pek sevmiyorum. Bir görsel kaydedip WebP olarak kaydedildiğinde, web tarayıcısı dışında destekleyen pek yer olmadığı için düzenlemeden ya da anlamlı biçimde kullanmadan önce dönüştürmem gerekiyor
    Sanki sadece ek bir adım dayatıyor gibi

    • İronik biçimde, Google ürünü olan Slides bile WebP görsellerini desteklemiyor
      Yine de destek daha da artarsa sorun olmayabilir. 20 yılda bir yeni format çıkmasına katlanabilirim
      .webm ise ortadan kalksa olur
    • Dönüştürme 2 saniye sürüyor. macOS'te kelimenin tam anlamıyla sağ tık menüsünde var; boyutu da daha küçük olduğuna göre pek sorun sayılmaz