Bir milyon satırda sanal kaydırma — asıl nerede tıkanıyor
· 4 dk okuma
Sanal kaydırmanın ders kitabı hali dört satır aritmetiktir. Bir boşluk elemanına
tüm satırların toplam yüksekliğini verin, scrollTop'u izleyin ve yalnızca
görünen alana düşen dilimi çizin. Bir milyon satır yaklaşık yirmi küsur DOM
düğümüne iner — bizimki tipik bir görünüm yüksekliğinde 24 satır çiziyor.
O kısım ilk denemede çalışır. Sonra bir milyon satırın en dibine kaydırırsınız ve son altı satır ortada yoktur.
Tarayıcı boşluk elemanınız hakkında yalan söylüyor
Bir elemanın yüksekliğini 40.000.000 piksel yapın ve geri okuyun. 40.000.000 almazsınız. Tarayıcılar bir elemanın ne kadar uzun olabileceğini sessizce sınırlar; sınır ne belgelenmiştir ne de her yerde aynıdır. Kendi ölçümlerimizden:
| Motor | Tavan |
|---|---|
| Chromium | 33.554.428 cihaz pikseli (%125 ekran ölçeklemesinde 26.843.542 CSS px) |
| Firefox | 17.895.697 |
40 piksellik bir milyon satır 40.000.000 piksellik içerik eder. Chromium size sessizce bunun 26,8 milyonunu verir. Artık kaydırma motoru ile DOM, 999.999. satırın nerede olduğu konusunda anlaşamaz — ve anlaşmazlık tam da kırpılan miktar kadardır. Satırlar "çizilmemiş" değildir; ulaşılamayan bir kaydırma konumuna eşlenmişlerdir, çünkü kaydırma çubuğu fiziksel olarak daha önce biter.
Dahası, tavan oynar. Cihaz pikseli cinsindendir, yani ekran ölçeklemesi onu değiştirir: aynı makine %100'de ve %125'te farklı sınırlara sahiptir. 4K monitörü ölçeklenmiş masaüstüyle kullanan biri sizden başka bir duvara toslar.
Tavanı ölçmeyin — altında bir değer seçin
Akla gelen çözüm yoklamaktır: bir eleman oluştur, tarayıcı kabul etmeyi bırakana kadar büyüt, çıkan sayıyı kullan. Denedik. Sabit bir sayıdan daha kötü, iki sebeple.
Birincisi, yoklama kararlı değil. Chromium'un, istenen 26.843.041 pikselin 26.843.040'ını koruduğu ölçüldü — bir eksik, ve hep aynı bir eksik değil. Sıkıştırma altında dönen değer yine kayıyor. Yani kaydırma modelinizi "neredeyse doğru" bir sayının üstüne kuruyorsunuz; bu, sayıların en kötüsüdür: biri son ekrana kaydırana kadar her şey çalışır.
İkincisi, yoklama her mount'ta, her makinede, sonsuza kadar bir sabiti yeniden keşfetmek için bir layout maliyeti ödetir.
Biz sıkıcı seçeneği tercih ettik:
export const DEFAULT_MAX_CANVAS_HEIGHT = 16_000_000;
On altı milyon CSS pikseli, Firefox dahil ve ölçeklenmiş ekranlar dahil her motorun sınırının rahatça altında. Bu yüksekliğin üstünde motor satırları piksellere birebir eşlemeyi bırakıp sıkıştırmaya geçiyor: tuval sabit boyutta kalıyor, kaydırma konumları bir orana göre satır indekslerine çevriliyor.
Kullanıcının fark edebileceği hiçbir şey kaybetmiyorsunuz. 400.000 satırda zaten tutamağın her pikseline yüz satır düşüyor; kimse sürükleyerek 712.449. satıra varmıyor. Kazandığınız şey, iddia ettiği kadar uzun olan bir kaydırma modeli.
İkinci arıza: "son" neresi sayılır
Tuval sıkıştırıldığında "dipte miyim?" sorusu artık
scrollTop + clientHeight === scrollHeight değildir. Kayan noktalı oranlar tam
eşitliğe oturmaz ve tarayıcılar uçlarda scrollTop'u farklı yuvarlar.
Dolayısıyla sona bir tolerans gerekir — fiziksel sonun yakınındaki, son sayılan bir piksel bandı. Bu olmadan son sayfa teoride ulaşılabilir, pratikte ulaşılamaz olur; kullanıcıların "son birkaç satır görünmüyor" diye bildirdiği şey tam olarak budur.
İlgili bir tuzak: grid'in altbilgisi veya toplam satırı görünüm alanının altını kaplıyorsa, alt kenara hizalanan bir satır onun arkasına düşer. Satır çizilmiştir, doğru konumdadır ve görünmez. Bu hiç de bir kaydırma hatası değildir — ama öyle sandığınızda öğleden sonranızı yer.
Az satır çizmek, hızlı olmak demek değil
Sanal kaydırma ne kadar DOM kurduğunuzu sınırlar. Çizimden önce yaptığınız işe hiçbir şey yapmaz — ve bir milyon satırda kareler orada kaybolur.
Bizi şaşırtan şuydu: worker için sütunları kodlamak. Bir milyon değer üzerinde düz bir döngü ana iş parçacığını 650 ms bloke ediyordu. Çizimde bir sorun yoktu; grid veriyi hazırlarken yanıt vermeyi bırakıyordu. O döngüyü, aralarda denetimi geri veren dilimlere bölmek en uzun bloğu 58 ms'ye indirdi — aynı toplam iş, tarayıcı girdiyi araya sokabilecek şekilde yayılmış.
Ders genelleşiyor: sanallaştırdıktan sonra kalan gecikmeniz neredeyse hiçbir zaman çizimde olmuyor. Veri kümesi bir milyon satır olmadan önce yazılmış, tüm veri üzerinde senkron geçen bir döngüde oluyor.
Ne test edilmeli
Bir grid geliştiriyor veya değerlendiriyorsanız, üç kontrol bunların çoğunu bulur:
- En büyük veri kümenizin en dibine kaydırın ve son satırları sayın. Yükseklik tavanını yakalayan kontrol budur.
- Bunu %125 ekran ölçeklemesinde yapın. Tavan cihaz pikseli cinsinden; %100'de saklanan hata burada ortaya çıkar.
- Tüm veri yüklüyken filtreye yazın ve düşen kareleri izleyin. Senkron tam tarama orada saklanır.
Bizim grid'imiz yukarıdaki düzeltme oturana kadar birinci kontrolde üç ayrı kez kaldı — ikisinde ölçülen tavana güvendiğimiz için, birinde boşluk elemanı render bloğunu taşırdığı için. Her seferinde listenin başından bakınca sorun yok gibi görünüyordu.
Kendiniz deneyin: Büyük Veri Performansı playground'u tarayıcıda 1K'dan 2M satıra kadar çalışıyor.