- Rust ile yazılmış WASM ayrıştırıcısı yapısal olarak hızlı olsa da, JS-WASM sınırındaki veri kopyalama ve serileştirme ek yükü performans darboğazı olarak ortaya çıktı
serde-wasm-bindgenüzerinden doğrudan nesne döndürme, JSON serileştirmesinden %9–29 daha yavaştı; bunun nedeni çalışma zamanları arasındaki ayrıntılı dönüştürme maliyetiydi- Tüm hattı TypeScript'e taşımak, aynı mimaride tek çağrı performansında 2.2–4.6 kat hızlanma sağladı
- Akış işleme tarafında cümle düzeyinde önbellekleme ile O(N²)→O(N) iyileştirmesi sayesinde toplam işlem hızında 2.6–3.3 kat artış elde edildi
- Sonuç olarak, WASM'in hesaplama yoğun ve düşük sıklıkta çağrılan işler için uygun, JS nesnesi ayrıştırma veya sık çağrılan fonksiyonlar için ise uygun olmadığı doğrulandı
Rust WASM ayrıştırıcısının yapısı ve sınırları
openui-langayrıştırıcısı, LLM'in ürettiği DSL'yi React bileşen ağacına dönüştüren 6 aşamalı bir hat olarak yapılandırılmıştır- Aşamalar:
autocloser → lexer → splitter → parser → resolver → mapper → ParseResult - Her aşama belirteçleştirme, sözdizimi analizi, değişken çözümleme, AST dönüşümü gibi işleri yapar
- Aşamalar:
- Rust kodunun kendisi hızlı olsa da, JS↔WASM arasında dize kopyalama, JSON serileştirme ve ters serileştirme her çağrıda gerçekleşir
- Girdi dizesi kopyalama (JS→WASM), Rust içinde ayrıştırma, sonuç JSON'unu serileştirme, JSON'u kopyalama (WASM→JS), JS tarafında ters serileştirme
- Bu sınır ek yükü toplam performansa hakim oldu; darboğaz Rust'ın hesaplama hızı değildi
serde-wasm-bindgen denemesi ve başarısızlığı
- JSON serileştirmesinden kaçınmak için, Rust struct'larını doğrudan JS nesnesi olarak döndüren
serde-wasm-bindgenuygulandı - Ancak %30 yavaşlama gözlendi
- JS, Rust struct bellek düzenini doğrudan okuyamaz ve çalışma zamanları arasındaki bellek yerleşimi farklı olduğundan alan bazında dönüştürme gerekir
- Buna karşılık JSON serileştirme, Rust içinde tek seferde bir dize üretir; JS tarafında ise optimize edilmiş
JSON.parseile işlenir
- Karşılaştırma sonuçları
Fixture JSON round-trip serde-wasm-bindgen Değişim simple-table 20.5µs 22.5µs -9% contact-form 61.4µs 79.4µs -29% dashboard 57.9µs 74.0µs -28%
TypeScript'e geçiş ve performans artışı
- Aynı 6 aşamalı yapı tamamen TypeScript'e taşındı, WASM sınırı kaldırıldı ve doğrudan V8 heap'i içinde çalıştırıldı
- Tek çağrı bazlı benchmark sonuçları
Fixture TypeScript WASM Hız artışı simple-table 9.3µs 20.5µs 2.2 kat contact-form 13.4µs 61.4µs 4.6 kat dashboard 19.4µs 57.9µs 3.0 kat - Yalnızca WASM'i kaldırmak bile çağrı başı maliyeti ciddi ölçüde düşürdü, ancak akış yapısındaki verimsizlik hâlâ sürüyordu
Akış ayrıştırmada O(N²) sorunu ve iyileştirme
- LLM çıktısı birden fazla parça hâlinde geldiğinde, her seferinde biriken tüm dizenin yeniden ayrıştırılması nedeniyle O(N²) verimsizliği oluşuyordu
- Örnek: 1000 karakterlik bir belgeyi 20 karakterlik parçalarla 50 kez ayrıştırmak → toplam 25.000 karakter işleme
- Çözüm olarak cümle düzeyinde artımlı önbellekleme (incremental caching) getirildi
- Tamamlanmış cümleler önbelleğe alınır, yalnızca devam eden cümle yeniden ayrıştırılır
- Önbellekteki AST ile yeni AST birleştirilerek sonuç döndürülür
- Tüm akış için benchmark
Fixture Naif TS Artımlı TS Hız artışı simple-table 69µs 77µs Yok contact-form 316µs 122µs 2.6 kat dashboard 840µs 255µs 3.3 kat - Cümle sayısı arttıkça önbellek etkisi büyüdü ve toplam işlem hacmi doğrusal olarak iyileşti
WASM kullanımına dair çıkarımlar
- Uygun olduğu durumlar
- Hesaplama yoğun ve etkileşimi az işler: görüntü/video işleme, şifreleme, fizik simülasyonu, ses kodekleri vb.
- Mevcut yerel kütüphanelerin taşınması: SQLite, OpenCV, libpng vb.
- Uygun olmadığı durumlar
- JS nesnelerine dönüştürülen yapılandırılmış metin ayrıştırma: serileştirme maliyeti baskın hâle gelir
- Kısa girdilerle sık çağrılan fonksiyonlar: sınır maliyeti hesaplamadan büyüktür
- Temel dersler
- Dil seçmeden önce darboğazın nerede olduğunu profillemek gerekir
serde-wasm-bindgenile doğrudan nesne aktarımı daha maliyetlidir- Algoritmik karmaşıklığı iyileştirmek, dil değiştirmekten daha etkili olabilir
- WASM ve JS heap paylaşmaz; dönüştürme maliyeti her zaman vardır
Nihai sonuç: TypeScript'e geçiş ve artımlı önbellekleme ile çağrı başına 2.2–4.6 kat, tüm akışta 2.6–3.3 kat performans artışı elde edildi
2 yorum
Amaç, ileri düzey Rust performans iyileştirme yazılarını hafifçe iğnelemek değil miydi..
Hacker News yorumları
Asıl mesele Rust yerine TypeScript değil, O(N²)'den O(N)'e indirilen akış algoritması düzeltmesi
Yalnızca ifade düzeyinde önbellekleme yapan bu değişiklik bile tek başına 3,3 kat iyileşme sağlamış
Dil seçimi ayrı bir konu; kullanıcı açısından hissedilen gecikme (latency) iyileşmesinin başlıca nedeni bu kısım
Başlık sanki bu ilginç mühendislik noktasını olduğundan küçük göstermiş
Yazının kendisi ilgi çekici ama bugünlerde aşırı tıklama odaklı başlıklardan bıkmış durumdayım
Yöntem, her çağrının süresini ölçüp medyanı (median) kullanmak; ama tarayıcı ortamında zamanlama saldırılarına karşı savunma mantığı olan JS motorlarında bunun doğruluğu soru işareti
“Kodu L dilinden M diline yeniden yazdım ve hızlandı” demek zaten beklenen bir sonuç
Çünkü bu, dolaşmış ve hatalı kararları düzeltme ve sonradan ortaya çıkan daha iyi yaklaşımları uygulama fırsatı veriyor
Aslında L=M olsa bile aynı şey geçerli; hız artışı dilden değil, yeniden yazma ve yeniden tasarlama sürecinden geliyor
Rust ve JS sınırında nesne serileştirme performansını iyileştirmeye çalışırken daha derine indim
serde'nin yaklaşımı performans açısından pek iyi görünmüyordu; bunu iyileştirme denememi blog yazımda anlattım
Open UI'ın neden WASM ile ilgili işler yapmadığını merak etmiştim
Ama bu yeni şirketin de Open UI adını kullanması kafa karıştırdı
Asıl Open UI W3C Community Group, 5 yılı aşkın süredir HTML için popover, özelleştirilebilir select, invoker command, accordion gibi standartlar üreten bir grup
Gerçekten harika işler yapıyorlar
“JSON gidiş dönüşünü atlayalım” denilerek serde-wasm-bindgen entegre edilmiş ama sonuçta bu bana ikili biçimde JSON'un yeniden icadı gibi görünüyor
Günümüzde V8'in JSON'u zaten çok iyi optimize edilmiş durumda ve simdjson gibi uygulamalar saniyede gigabaytlar düzeyinde işleyebiliyor
JSON'un darboğaz olma ihtimali düşük görünüyor
O blogun tasarımını gerçekten çok beğendim
Kaydırma konumuna göre başlıkları vurgulayan “scrollspy” kenar çubuğu özellikle çok iyiydi
Claude'un söylediğine göre sanırım fumadocs.dev ile yapılmış
Rust WASM ayrıştırıcısının amacını pek anlayamadım
Yazıda bu kısım net olmadığı için daha fazla açıklama gerekiyor
Bu da muhtemelen prompt injection kaynaklı bilgi sızıntısını önlemek için
Ayrıştırıcı, LLM'den akış halinde gelen parçaları derleyip gerçek zamanlı UI oluşturuyor
Önceden her parçada ayrıştırıcı en baştan yeniden başlatılıyordu; bunu artımlı işleme yaklaşımına çevirince (Rust→TypeScript taşıması sırasında) performans büyük ölçüde artmış
TypeScript'in bugünlerde Golang tabanlı çalıştığına dair bir merakım vardı
Şaka bir yana, tekrar Rust ile yeniden yazılırsa belki bir 3 kat daha performans artışı gelir /s