WebAssembly, JavaScript’i nasıl hızlı çalıştırabiliyor?
(bytecodealliance.org)Giriş
-
JS tarayıcıda çalıştırıldığında, tarayıcının JS motoru iyi optimize edildiği için yürütme hızlıdır; ancak bugünlerde JS başka ortamlarda da çok kullanılıyor. (serverless, oyun konsolları, iOS vb.)
-
WASM, bu tür çalışma zamanlarında JS’in hızlı çalışmasını sağlayan bir teknolojidir.
Çalışma şekli
-
Bir JS motoru varsa, JS kodu yorumlayıcı ve JIT derleyici gibi bileşenler üzerinden bytecode’a dönüştürülür.
-
JS motoru olmayan ortamlarda JS motorunu kodla birlikte dağıtmak gerekir; JS motorunu bir WASM modülü olarak dağıtarak bunu farklı ortamlarda taşınabilir hale getirebilirsiniz.
-
JS kodu, WASM motorunun içindeki izole edilmiş JS motorunda çalışır.
-
WASM motorunun kullandığı JS motoru SpiderMonkey’dir; Firefox da bunu kullanır.
-
WASM kendi başına makine kodu üretemediği için JS’ye derleme sürecinden geçmesi gerekir.
-
Ama JIT kullanılamadığı için normalde WASM’ın yavaş olması beklenir. O halde WASM, JS çalıştırmayı tam olarak nasıl “hızlandırıyor”?
WASM nerede kullanılıyor?
iOS’ta (veya JIT kullanılamayan ortamlarda) JS kullanmak
- Oyun konsolları, ayrıcalıksız iOS uygulamaları, akıllı TV’ler vb. güvenlik gerekçesiyle JIT kullanamaz.
(→ JIT derlemenin güvenlik sorunları taşımasının çok doğal bir şey olduğu söyleniyor ama nedenini ben de aradığım halde pek bulamadım.)
- Bu yüzden böyle yerlerde yorumlayıcı kullanmak gerekir; ama aslında bu platformlarda çalışan uygulamalar çok uzun süre çalışır ve kod miktarı da fazla olur, bu yüzden yorumlayıcı nedeniyle yavaşlamayı önlemek daha doğrudur.
- Yorumlayıcının performans düşüşü sorunundan kaçınırken JS kullanmak nasıl mümkün olabilir?
Serverless’ta JS kullanmak
- Serverless ortamlarında JIT vardır ama sorun, cold start süresinin uzun olması ve bunun gecikmeyi artırmasıdır. (yalnızca motoru yüklemek bile en az 5 ms)
- Cold start süresini gizleyen optimizasyon teknikleri var; ancak ağ katmanı iyileştikçe (ör. QUIC) bunların anlamı azalıyor ve ayrıca birden fazla serverless işlev aynı anda çalıştırıldığında bu optimizasyonlar pek işe yaramıyor.
- Instance reuse ile cold start süresinden kaçınmak da mümkün, ancak bu da istekler arasında durumun paylaşılması anlamına gelir ve bir güvenlik riskidir.
- Bu nedenlerle pratikte, en iyi uygulamaları izlemeyip tek bir serverless işlevinin içine çok fazla şey doldurmak da giderek yaygınlaşıyor.
- Yani sadece cold start sorunu çözülse bile, bundan kaçınmak için kullanılan pek çok tekniğe gerek kalmaz ve birçok sorun da çözülür.
- WASM, JS’i sarıp izole eder; WASM’ın kendi kodu da kısa ve basit olduğu için denetlenmesi kolaydır ve güvenlik riski azalır.
JS motoru zamanını en çok nerede harcıyor?
Başlatma aşaması
- (motor başlatma) serverless ile ilgili kısım budur. Motorun önce kendini hazırlaması ve built-in fonksiyonları ortama eklemesi gerekir. Serverless’ta cold start’ın yavaş olmasının nedenlerinden biri de budur.
- (uygulama başlatma) fonksiyonları bytecode’a parse etme, değişkenler için bellek ayırma, değişkenlere değer atama
Çalışma zamanı aşaması
- Bu noktadan sonraki throughput çeşitli koşullardan etkilenir.
- hangi dil özelliklerinin kullanıldığı
- kodun JS motorunun bakış açısından öngörülebilir davranıp davranmadığı
- ne tür veri yapılarının kullanıldığı
- kodun, JS motorunun optimize edici derleyicisinden faydalanacak kadar uzun süre çalışıp çalışmadığı
Bir JS motorunu hızlandırmak demek hem başlatma hem de çalışma zamanı aşamalarını hızlandırmak demektir. Daha doğrusu, başlatma için gereken süreyi azaltmak ve çalışma zamanında throughput’u, yani kodun işlenme hızını artırmaktır.
Başlatma süresini azaltmak
-
WASM, başlatma süresini azaltmak için Wizer adlı bir pre-initializer kullanır. (küçük uygulamalar baz alındığında JS isolate’a kıyasla JS on WASM yaklaşık 13 kat daha hızlıdır)
-
Kod dağıtılmadan önceki build aşamasında pre-initializer, tüm JS kodunu bir kez başlatma aşamasına kadar çalıştırır.
-
Böylece JS motorunun lineer belleğinde JS kodları bytecode olarak depolanmış olur ve bellek ayırma da tamamlanmış olur.
-
Bunun birebir kopyası alınıp WASM’ın data section’ına eklenir.
-
-
JS motoru instantiate edildiğinde data section’daki tüm verilere erişebilir. Belirli bir belleğe ihtiyaç duyarsa data section’dan kopyalayabilir. Bu yüzden başlangıç süresine gerek kalmaz; buna da pre-initialization denir.
-
Şu anda data section, JS motoruyla aynı modüle ekleniyor; ancak gelecekte module linking kullanılarak data section’ın ayrı bir modül haline getirilmesi ve birden fazla uygulamanın JS motorunu paylaşabilmesi planlanıyor.
-
Aslında bu pre-initialization tekniğinin yalnızca JS motoruna özgü olması gerekmiyor; Python, Ruby, Lua gibi her türlü çalışma zamanı için kullanılabilecek bir kavram.
Throughput’u artırmak
-
JS kodu yalnızca kısa bir süre çalışıyorsa zaten JIT’ten geçmeyeceği için WASM’ın throughput’u da tarayıcıyla aynı olacaktır. Ancak uzun süre çalışan kodlarda, JIT’in devreye girip girmemesi throughput farkını büyük ölçüde belirler.
-
WASM JIT kullanamadığı için bunun yerine AOT(ahead-of-time) derleme kullanıyor, ama JIT’ten alınabilecek teknikleri de mümkün olduğunca benimsiyor.
-
JIT’in optimizasyon tekniklerinden biri inline caching’dir. Geçmişte çalıştırılmış kod parçalarını saklayıp yeniden kullanmak anlamına gelir.
-
WASM’da, JS’te sık kullanılan kalıplar stub olarak önceden hazırlanmıştır. Örneğin obje property’sine erişim.
-
Normalde obje property erişimini düzgün yapmak için shape ve offset bilgisi gerekir, ama bunlar AOT ile önceden bilinemez.
-
Ancak shape ve offset’i parametre olarak alıp property’ye erişen bir stub önceden hazırlanabilir. Bu stub kodu birçok yerde yeniden kullanılabilir.
-
-
WASM bu tür common pattern’lerin tamamını stub haline getirir. Bu, gerçek JS kodunun nasıl göründüğünden bağımsızdır. Böylece JS motorunun üretmesi gereken makine kodu azalır, başlatma süresi kısalır ve cache locality iyileşir.
-
Yalnızca 2kb’lık bu tür stub’lar hazırlandığında bile gerçek JS kodunun yaklaşık %95’ini kapsayabildiği görülmüştür.
-
Bu tür teknikler ahead-of-time, yani kodun içeriği bilinmeden (profiling olmadan) optimizasyon yaptığı için, profiling daha ileri götürülebilirse JIT’te olduğu gibi daha fazla optimizasyon alanı da oluşabilir.
- Ancak profiling’in kendisi de kolay bir iş olmadığı için bunun üzerinde çalışılıyor.
2 yorum
JIT’nin güvenlik sorunlarıyla ilgili olarak, daha önce burada tanıtılan MS Edge ekibinin blog yazısında da ilgili içerikten bahsedilmişti. Temel olarak JIT motorları karmaşık olduğu için saldırı yüzeyini artırıyor; ayrıca JIT’nin performans artışı için uyguladığı spekülatif optimizasyon (Speculative Optimization) gibi yöntemlerin, belirli türde güvenlik sorunlarını tekrar tekrar ortaya çıkarma eğiliminde olduğu anlaşılıyor. Bu nedenle web tarayıcılarındaki güvenlik açıkları arasında JIT ile ilgili olanların oranının oldukça yüksek olduğu söyleniyor.
https://tr.news.hada.io/topic?id=4771
https://microsoftedge.github.io/edgevr/posts/Super-Duper-Secure-Mode/
https://docs.google.com/spreadsheets/d/…
Ah teşekkürler! Aslında GeekNews'e bakmamıştım.