· kaynak dev.to (home feed)
Tam kanvas getImageData belleği ikiye katlıyor, Worker'lar boyut limitlerini yükseltemiyor
Chromium, WebKit ve Firefox motor sürümleri üzerinde alınan işletim sistemi düzeyindeki ölçümler, tam kanvas okumasının bitmap'in ikinci bir kopyasını eklediğini ve bir Worker içindeki OffscreenCanvas'ın motor başına aynı boyut sınırlarını devraldığını gösteriyor.

Bir okuma gerçekte neye mal oluyor
Aynı gün yayımlanan iki dev.to yazısı, üç motor sürümü üzerinden Apple M4 ve 16 GB RAM'e sahip bir makinede yapılan ölçümleri raporluyor: Chromium 149 açık kaynak sürümü, bir WebKit 26.5 sürümü ve bir Firefox 151 sürümü. Her iki yazar da bunların hiçbirinin insanların gerçekte indirdiği tarayıcılar olmadığını vurguluyor; yani rakamlar tarayıcıları değil motorları tarif ediyor. Sayfanın içinde hiçbir şey kanvas belleğini gözlemleyemediği için ilk yazı, ölçümü işletim sistemi düzeyinde, bir çalıştırmanın tüm tarayıcı süreçlerindeki phys_footprint değerlerini toplayarak yaptı.
Ana bulgu, alışık olduğumuz genişlik × yükseklik × 4 tahmininin yalnızca kanvasın kendisini kapsadığı. Tam kanvas getImageData, aynı boyutta ikinci bir tampon bellek ayırıyor. 16384×16384, yani kabaca 268 megapiksel boyutunda, Chromium bir doldurma işleminin ardından 1025 MiB, okumanın ardından ise 2050 MiB ölçtü; Firefox aynı ikilinin yaklaşık bir yüzde yakınında kaldı. WebKit sürümü 1206 MiB ve 3265 MiB ile aykırı durumdaydı ve küçük yüzeylerde de onlarca MiB ek yük taşıyordu: doldurulmuş bir 1024×1024, formülün 4 MiB öngördüğü yerde 44.6 MiB'e mal oluyordu.
Zamanlama ayrıntıları bütçe hesaplayan herkes için önemli. Her motorda bir kanvas oluşturup çizim yapmadan getContext('2d') çağırmak 1 MiB'nin altına mal oldu; yani bitmap oluşturma anında değil, ilk çizimde ortaya çıkıyor. JS heap'i tüm bunlara kör: Chromium'daki 8192×8192 dizisi sırasında işletim sistemi düzeyindeki bellek 0.3 MiB'den 256.7 MiB'ye, oradan 512.9 MiB'ye çıkarken JSHeapUsedSize yalnızca yaklaşık 949 KB'den yaklaşık 1.1 MB'ya oynadı. Boyutları sıfırlamak ve referansı bırakmak, Chromium'da belleği 0.8 saniye içinde işletim sistemine geri vermedi; yazar bunu, belleği serbest bırakmak ile onu işletim sistemine iade etmenin ayrı anlar olduğuna dair bir hatırlatma olarak çerçeveliyor, sızıntı kanıtı olarak değil.
Olmayan Worker çözümü
İkinci yazı, görsel yükleme kod incelemelerinde tekrar tekrar ortaya çıkan bir öneriyi hedefliyor: devasa bir fotoğraf boş bir dışa aktarım ürettiğinde çizimi OffscreenCanvas ile bir Worker'a taşıyın. Ölçümler, iş parçacığının kapasiteye hiçbir fark yaratmadığını söylüyor. Yazar, çizim başarısız olana kadar boyutları ikiye katladı, ardından bir kanvas elementi, ana iş parçacığındaki bir OffscreenCanvas ve bir Worker içindeki bir OffscreenCanvas için tam piksele kadar ikili arama yaptı. Üç motorun üçünde de eşik, üç yüzey türünün tümünde birbiriyle eşleşti.
Sınırların kendisi motora göre değişiyor. Chromium 149 ve WebKit sürümü alanı 268.435.456 pikselle sınırlıyor; yani 16384×16384 çiziliyor ama 16384×16385 başarısız oluyor; Firefox sürümü 23168×23168'ye kadar ulaşabiliyor. Maksimum tek kenar, Chromium ve Firefox sürümlerinde 65.535 pikselde, WebKit'te 4.194.303'te duruyor. Tavan motora göre değişiyor, iş parçacığına göre asla; yazarın yorumu, sınırın talep edene değil bitmap tahsisine bağlı olduğu yönünde.
Limit aşan kanvaslar nasıl başarısız oluyor
Başarısızlık biçimlerinde iş parçacıkları işleri gerçekten değiştiriyor. Sayfada, Chromium'daki limit aşan bir kanvas hiçbir hata bildirmeden düzgünce boş bir dama tahtası olarak görüntülendi ve toBlob null döndürdüğünde kontrolsüz bir sarmalayıcı, içinde "null" yazan 4 baytlık bir PNG üretti. Limit aşan kanvaslar kısmen de görüntüleyebiliyor: sol ve orta kısım doğru görünürken uzak uç boş kalıyor; bu yüzden yazarın doğrulama sondağı sol üstteki piksel yerine bir köşe pikselini okuyor.
Bir Worker içinde contextlost tetikleyecek bir element olmadığından, reddedilen bir convertToBlob tek görünür sinyal. Chromium, genişlik ve yükseklik hâlâ istenen değerleri raporlarken boyutun sıfır olduğunu iddia ederek IndexSizeError ile reddediyor; WebKit EncodingError ile, Firefox ise NS_ERROR_FAILURE ile reddediyor; ikincisi limit aşan getImageData okumalarında da sıfırlar döndürmek yerine fırlatılıyor. WebKit'in kanvas alanı sınırını aşma konsol uyarısı, bir Worker'dan yapılan 104 alan aşma sondajının yalnızca birinde ortaya çıktı.
Pratik çıkarımlar
Yazarın artık kullandığı bütçe, piksel × 4 bayt ve pikselleri geri okuyan her işlemde ikiye katlanmış hali. Bu, Chromium ve Firefox ile yaklaşık bir yüzde içinde eşleşti ama WebKit aştı — 8192×8192'de okuma sonrası 823.3 MiB, öngörülen 512'ye karşı — dolayısıyla tavan, motor başına bir çarpan tahmin etmek yerine formülün altına sabitleniyor. Boyutlar motor başına bir tabloya karşı kontrol edilip bir Worker'a gönderilmeden önce küçültülüyor ve reddedilen bir convertToBlob, hatanın yanına istenen boyut da log'lanarak "çok büyük" olarak ele alınıyor.
Yazarlar net sınırları sıralıyor: yayın tarayıcıları test edilmedi, telefonlar yok, GPU destekli kanvaslar kapsam dışıydı, meşgul bir makinede güvenlik sınırlarında durulduğu için bir çöküş eşiği belirlenmedi ve Worker belleği ayrıca ölçülmedi.
Neden önemli
Görsel işleme hatları, bu ölçümlerin çürüttüğü iki varsayım taşıma eğiliminde: bir kanvasın genişlik × yükseklik × 4'e mal olduğu ve bir Worker'ın aşırı büyük görseller için bir kaçış kapısı olduğu. İlki, getImageData çalıştığında tam bir kopya kadar eksik sayıyor; ikincisi ise kapasiteyi değiştirmeden başarısızlık biçimini değiştiriyor. Ortaya çıkan hatalar sessiz — fırlatılan istisnalar yerine boş dışa aktarımlar ve null blob'lar — ve sayfa düzeyindeki heap metriklerine görünmez. Güvenilir kontroller, işletim sistemi düzeyinde bellek ölçümü ve çizimin gerçekten yapıldığını doğrulamak için görüntülenmiş bir köşe pikselini yoklamak; hem de gerçekte yayın yaptığınız tarayıcılarda.
- #canvas
- #memory
- #offscreen-canvas
- #web-workers
- #browser-engines