- WebAssembly ने Photoshop जैसे बड़े C++ प्रोग्रामों को वेब पर लाने में सफलता पाई है, लेकिन DOM-केंद्रित ऐप्स में JavaScript से अलग प्रोग्रामिंग मॉडल के कारण इसका प्रसार सीमित रहा
- ब्राउज़र में Wasm GC और reference types का समर्थन Python, Scheme जैसी managed memory भाषाओं के लिए अवसर खोलता है, लेकिन वेब पर ट्रांसफर साइज़ ही अपनाने की बाधा बन जाता है
- Go का साधारण Wasm प्रोग्राम 2MB होता है, और import जोड़ने पर 10MB से अधिक हो सकता है; Pyodide REPL लगभग 20MB डाउनलोड करता है, जो सामान्य वेब ऐप्स के लिए बोझिल है
- Hoot Scheme compiler, GC-सपोर्टेड Wasm को target करता है और न्यूनतम “main” compilation unit को लगभग 70KB तक घटा चुका है; सहायक compilation units 1KB से भी कम हो सकती हैं
- प्रभावी ट्री शेकिंग सिर्फ unreferenced functions हटाने से आगे की चीज़ है; यह flow analysis और standard library design के साथ जुड़ी compiler समस्या है
वेब पर WebAssembly ने जहाँ सफलता पाई
- WebAssembly वेब पर शुरुआती उम्मीदों जितना व्यापक नहीं फैला, लेकिन कुछ क्षेत्रों में सीमित सफलता मिली है
- इसका प्रमुख उदाहरण Photoshop जैसा बड़ा C++ प्रोग्राम है जिसे वेब पर लाया गया
- Figma का भी 5 साल पहले Wasm उदाहरण के रूप में उल्लेख हुआ था, लेकिन अब वह Wasm को ज़्यादा प्रमुखता से नहीं दिखाता
- C++ या Rust से compile हुई छोटी NPM libraries के भीतर Wasm का इस्तेमाल अक्सर होता है
- Blazor कुछ इन-हाउस enterprise apps में इस्तेमाल हो सकता है, लेकिन इसकी मार्केटिंग बढ़ा-चढ़ाकर भी की गई हो सकती है
- Unreal Engine का 3D FPS demo 5 साल पहले के एक पुराने major release पर आधारित प्रयोग था, और मौजूदा Unreal 5 WebAssembly target को support नहीं करता
DOM-केंद्रित ऐप्स में Wasm क्यों अटका
- WebAssembly वेब के बाहर के environments में सफल है और वेब platform पर भी इसका महत्व बढ़ सकता है, लेकिन वेब पर इसे अभी भी निराशा के दौर से बस निकलता हुआ माना जा सकता है
- Wasm उन कामों में मजबूत है जो JavaScript अच्छी तरह नहीं कर पाता, या जहाँ client और server के बीच shared implementation चाहिए
- DOM-केंद्रित ऐप्स में Wasm सफल नहीं हो पाया
- wordpress.com frontend को Wasm में दोबारा लिखने की बात नहीं होती
- वेब का मुख्य programming model dynamic types और managed memory वाला JavaScript है
- WebAssembly 1.0 को static types और linear memory के इर्द-गिर्द डिज़ाइन किया गया था
- Wasm से DOM तक पहुँचना इतना झंझटभरा था कि यह काम सिर्फ बहुत उत्साही Wasm समर्थक ही उठा सकते थे
- C# जैसी भाषाओं में garbage collector भी साथ ship करना पड़ता है, और यही बात C/Rust के बाहर की भाषाओं के Wasm adoption में बाधा रही
Wasm GC के बाद भी बची ट्रांसफर साइज़ की समस्या
- आने वाले कुछ महीनों में ब्राउज़र reference types और garbage collection support देने वाले हैं
- Chrome और Firefox पहले से Wasm GC दे रहे हैं
- Safari भी Asumu Takikawa के काम की वजह से अब दूर नहीं लगता
- Wasm GC ऐसा बदलाव है जो ज़्यादा भाषाओं को WebAssembly support के लिए अपने toolchains update करने पर मजबूर करेगा
- वेब Wasm के सफल होने के लिए compilers को छोटा कोड बनाना होगा
- अगर किसी भाषा का toolchain ट्रांसफर के लिहाज़ से कुछ KB के उपयोगी Wasm files बना सके, तो यह बड़ी बढ़त होगी
- नहीं तो उसे बढ़ी-चढ़ी उम्मीदों या बंदी user base पर निर्भर रहना पड़ेगा, और अगला समाधान मिलने तक अस्थिर संतुलन में रहना होगा
- JavaScript ecosystem में payload size और bloat घटाने के लिए पहले से बड़ा tooling उद्योग मौजूद है
- esbuild जैसे bundlers कई JS modules को एक file में बाँधते हैं
- वे सिर्फ इस्तेमाल होने वाले functions और data types शामिल करने की कोशिश करते हैं
- minification जैसी size reduction रणनीतियाँ भी लागू की जाती हैं
ट्री शेकिंग नाम का जाल
- ट्री शेकिंग में यह दृश्य रूपक है कि किसी page के लिए ज़रूरी code बचा रहे और बाकी गिर जाए
- इस रूपक में modules को शाखाएँ और definitions को पत्तियाँ मान लिया जाता है, लेकिन असली पेड़ को तना पकड़कर हिलाने से यह नहीं पता चलता कि कौन-सी शाखा ज़रूरी है और कौन-सी नहीं
- नाम खुद हमें गैर-ज़रूरी code हटाने की दिशा में सोचने को मजबूर करता है, लेकिन algorithmic नज़रिए से ज़रूरी code को बनाए रखने वाले fixed point को खोजना ज़्यादा सही है
- फिर भी ट्री शेकिंग नाम इतना असरदार है कि बागवानी और algorithm, दोनों ही अर्थों में गलत होने के बावजूद यह चलता रहता है
भारी runtime से बनने वाली साइज़ बाधा
- जिन भाषाओं का runtime भारी होता है, उनमें अधिकतम ट्री शेकिंग बड़ी प्राथमिकता नहीं रही
- Go के WebAssembly support में सबसे साधारण program भी golang wiki के अनुसार 2MB है
- import जोड़ने पर यह 10MB से ऊपर जा सकता है
- Python के WebAssembly port Pyodide के REPL उदाहरण में लगभग 20MB data डाउनलोड होता है
- यह साइज़ tech demos या बहुत समृद्ध applications के लिए ठीक हो सकता है, लेकिन सामान्य वेब development विकल्प बनना मुश्किल है
वैकल्पिक toolchains और platform-अनुकूल implementations
- Go का built-in Wasm support और Pyodide, upstream toolchains से निकले हैं, और server पर binary size उतनी महत्वपूर्ण नहीं हो सकती
- छोटे devices को target करते समय अलग implementations सामने आती हैं
- TinyGo का Wasm backend 1KB से कम तक जा सकता है
- ऐसे वैकल्पिक toolchains अक्सर सीमाएँ या विशेषताएँ साथ लाते हैं
- DOM environment में चलने वाला Wasm-target Python program, “native” Python program जैसा नहीं रह सकता
- Toolchain लेखक वही भाषा देने की कोशिश करते हैं, लेकिन standard library implementation अलग हो सकती है
- ClojureScript developers भी संभव हो तो Clojure से अंतर वाले दस्तावेज़ हटाना चाहेंगे, और अगर Wasm, ClojureScript का व्यावहारिक target बनता है, तो यह संभव हो सकता है
Hoot Scheme की ट्री शेकिंग पद्धति
- GC support के बाद Wasm, Python जैसी भाषाओं में DOM programming की कल्पना संभव बनाता है, लेकिन व्यापक उपयोग के लिए छोटे modules ज़रूरी हैं
- Hoot Scheme compiler GC वाले Wasm को target करता है
- फिलहाल न्यूनतम “main” compilation unit लगभग 70KB है
- लक्ष्य इसे और नीचे लाना है
- exception handlers जैसी runtime सुविधाएँ main module से import करने वाली सहायक compilation units 1KB से कम हो सकती हैं
- Hoot compiler user code के आगे prelude जोड़ता है
- ट्री शेकिंग कई चरणों में होती है
- partial evaluation unused bindings को सिर्फ effect evaluate करके हटा सकता है
- fixing letrec भी ऐसा ही काम करता है
- CPS program को बार-बार traverse करता है और सिर्फ referenced functions, values, और control-flow edges का पीछा करता है
- explicit dead-code elimination pass, दूसरी optimizations के बाद बची unused, effect-free assignments को हटाता है
- लगभग raw WebAssembly में लिखी standard library की definitions सिर्फ ज़रूरत पड़ने पर ही final binary में शामिल होती हैं
आसान हटाना और कठिन हटाना
- functions या closures जैसी procedure definitions तुलनात्मक रूप से संभालना आसान हैं
- code जिन functions को reference करता है, सिर्फ उन्हें शामिल करना होता है
- Scheme जैसी भाषा में सिर्फ इतना भी काफ़ी असर डाल सकता है
- तुरंत सामने आने वाली कठिनाइयाँ तीन हैं
-
letrec* evaluation model
- prelude definitions का scope recursive है, लेकिन उसमें क्रम भी है
- binding values पहले defined values को call या reference कर सकती हैं, और बाद में defined values को capture भी कर सकती हैं
- अगर किसी binding value को evaluate करने के लिए बाद में define होने वाली value को reference करना पड़े, तो यह error है
- procedures में यह आम तौर पर समस्या नहीं होती, लेकिन non-procedure definitions में compiler शायद यह साबित न कर पाए कि “यह सिर्फ पहले वाले bindings को reference करता है”
- ऐसे मामलों में fixing letrec reloaded algorithm set! किए गए bindings को छोड़ सकता है, और इन्हें हटाने के लिए सूक्ष्म DCE pass चाहिए
-
record type का vtable
- कुछ non-procedure definitions record types होती हैं
- record types में vtable होता है, जो record को print करने या instance check जैसी चीज़ें संभालता है
- vtable callbacks, भले ही वास्तव में इस्तेमाल न हों, बहुत सारा code जीवित बनाए रख सकते हैं
-
polymorphic output functions
displayजैसी polymorphic functions ज़रूरी code की सीमा को बहुत बढ़ा देती हैं- string छापने के लिए
displaycall करने पर पूरा buffered I/O infrastructure आ जाता है displayकुछ भी print कर सकता है, इसलिए bitvector, pair आदि कई cases का code भी साथ खिंच आता है- अगर सिर्फ strings के लिए
write-stringcall करें तो general data printing code से बचा जा सकता है, लेकिन ports जैसे general buffered I/O facilities फिर भी शामिल होते हैं
इष्टतम ट्री शेकिंग एक flow analysis समस्या है
- इष्टतम ट्री शेकिंग अंततः flow analysis की समस्या है
- अगर program में bitvector कभी आता ही नहीं, तो
displayके भीतर bitvector संभालने वाला code dead code हो सकता है - यह जानने के लिए पता होना चाहिए कि
displayकिन तरह के arguments के साथ call होता है, और इसके लिए high-level flow analysis चाहिए - Python में समस्या और कठिन हो जाती है
- object-oriented dispatch higher-order programming है, इसलिए
foo.barका अर्थfooके वास्तविक स्वरूप पर निर्भर करता है - Python की lookup प्रणाली Scheme से भी ज़्यादा dynamic है, और
__getattr__जैसे methods जगह-जगह इस्तेमाल हो सकते हैं - व्यवहार में flow analysis शायद ऐसी dynamic lookups को बाहर कर सके
- Python में ट्री शेकिंग का लक्ष्य lexical bindings वाला कोई बड़ा term नहीं, बल्कि modules के जटिल समूह हैं
- यह JavaScript जैसा है, लेकिन Python में tree-shaking bundlers का ecosystem स्थापित नहीं है
- object-oriented dispatch higher-order programming है, इसलिए
वेब Wasm भाषा toolchains की शर्तें
- Wasm GC, JavaScript के अलावा दूसरी भाषाओं में DOM programming संभव बना सकता है
- लेकिन व्यापक उपयोग के लिए resulting Wasm modules छोटे होने चाहिए
- हर भाषा toolchain में भारी निवेश की ज़रूरत है
- ऐसा निवेश अक्सर वैकल्पिक toolchains के रूप में दिखता है, जिनमें प्रयोगात्मक tree-shaking algorithms शामिल होते हैं
- वैकल्पिक standard libraries को इस तरह डिज़ाइन करना होगा कि tree shaker बेहतर काम कर सके
1 टिप्पणियां
Hacker News टिप्पणियाँ
openEtG के Wasm blob (card game engine) को 400KB से कम रखते हुए card text generation जैसी काफी logic को Wasm में ले जाया गया, और इसे Rust में लिखा गया था
size घटाने के लिए floating-point के बजाय fixed-point arithmetic इस्तेमाल करना, hashmap की जगह vector पर जाना, strings से बचना,
talcजैसे छोटे allocator का इस्तेमाल करना, और dependencies घटाने जैसी देखभाल करनी पड़ीअभी केवल
randऔरfxhashइस्तेमाल हो रहे हैं, औरrandभी शायद हटाया जा सकता है;fxhashका इस्तेमाल सिर्फ game state hash से desynchronization है या नहीं यह जाँचने के लिए होता हैgeneric instances के प्रकार भी घटाए, ताकि जब
Vecपहले से है तोBox<[i16]>जैसे types अतिरिक्त रूप से न खिंच आएँ; floating-point और hashmap हटाने से भी type diversity घटाने में मदद मिलीalgorithms भी size को ध्यान में रखकर design किए गए; उदाहरण के लिए, bit-packed lookup table से adrenaline mechanism encode किया गया, जिसमें कम attack power वाले creatures ज़्यादा बार attack करते हैं
uncompressed values store करने की लागत और decoding logic की लागत की तुलना की गई, और AI evaluation में WebAssembly पर 128 की तुलना में 64 ज़्यादा efficient तरीके से encode होता है, इसलिए 6-bit fixed precision इस्तेमाल किया गया
targeting mechanism भी पहले AST रूप में था, जहाँ हर predicate को enum के रूप में और AND/OR को expression slices के रूप में रखा जाता था; अब expression को Polish notation में 32-bit integer में encode किया जाता है, और AND/OR को 2 bits व predicates को 6 bits में रखा जाता है
यहाँ reverse Polish notation की तुलना में Polish notation बेहतर रही, क्योंकि AND/OR की short-circuit evaluation संभव थी
काम पर ऐसी problem के लिए, जहाँ maximum resolution requirements पता हैं—जैसे sub-millimeter position precision की जरूरत नहीं है—सोच रहा हूँ कि fixed-point मदद करेगा या नहीं, इसलिए इससे जुड़े issues के बारे में और सुनना चाहूँगा
wasm-optइस्तेमाल करना हैयह Wasm size को लगातार लगभग 20–30% घटाता दिखता है: https://github.com/WebAssembly/binaryen
browser को Wasm bundle serve करते समय Brotli compression इस्तेमाल करना और web server को Brotli-compressed files इस्तेमाल करने के लिए configure करना भी अच्छा है
nginx हो तो यह एक line के change से हो सकता है, और Brotli Wasm bundle size को लगभग 3 गुना घटाता है तथा gzip से काफी बेहतर है
इसमें deep learning forward/backward pass, reinforcement learning algorithms, और dynamics simulation तक सब शामिल था
अभी भी यह भारी लगने लायक नहीं है, लेकिन कितना और छोटा बनाया जा सकता है यह जानने की उत्सुकता है, इसलिए जल्द ही इसे घटाकर देखना चाहता हूँ
floating-point से बचकर fixed-point arithmetic से space बचता है—यह बात कैसे सही बैठती है, समझना चाहता हूँ
खासकर नंबर 6 जैसी
VecकोBoxकी तरह इस्तेमाल करने वाली approach से बड़ी बचत होगी, ऐसा नहीं लगताट्री शेकिंग नाम काफी गलत नामकरण लगता है
Virgil compiler इसे “reachability analysis” कहता है और इसे compile model में built-in रखता है
compiler program और library code को parse करता है, type check करता है और initialization code चलाता है, लेकिन उसके बाद main entry point से खोजकर केवल reachable code का ही analysis करता है और उसे final binary में डालता है
बिना runtime system के, सिर्फ एक
mainfunction वाले program भी अच्छी तरह बनाता है, और runtime system सिर्फ stack traces और garbage collection के लिए चाहिए, इसलिए चाहें तो उसे छोड़ा जा सकता हैसबसे पहला मिला उदाहरण 1992 का Lucid Common Lisp 4.1 का Treeshaker tool था, जो UNIX के लिए commercial Common Lisp implementation था
Lucid CL में image की concept थी, जो running Lisp heap का saved memory dump होता था, और application image और runtime से मिलकर बनती थी
image आमतौर पर memory में मौजूद लगभग सारा code और data शामिल करती थी, इसलिए deployment के लिए छोटी image बनाना चाहा गया, और Treeshaker image save करने से पहले “unused” माने गए code और data को हटा देता था
यह reachable Lisp data और code के graph में connections की pruning करता था, फिर GC या special code garbage collect करके memory घटाता था और उसे छोटी image के रूप में dump करता था
इसलिए Treeshaker कोई compiler tool नहीं, बल्कि Lisp heap से unused code और data हटाने वाला tool था
default Lisp image में compiler, interpreter और REPL implementation तक शामिल होते थे, इसलिए running program को interrupt करके REPL में जाने पर image से restore हुई heap के अंदर का सारा code अब भी इस्तेमाल किया जा सकता था
इसलिए compiler या REPL तक हटाने का मतलब बनता था
कौन-सा code हटाया जा सकता है, यह तय करने के लिए reachability analysis आम तौर पर इस्तेमाल होता है, लेकिन analysis खुद code नहीं हटाता; हटाना बाद का step है
“tree shaking” आम तौर पर function-level removal का संकेत देता है, जबकि dead-code elimination conditional expression की branches हटाने जैसी कहीं ज्यादा fine-grained level पर भी संभव है और अलग-अलग static analyses पर आधारित हो सकता है
पहली बार देखते ही, बिना और जांच किए, इसका मतलब तुरंत समझ आ गया था
पेड़ को हिलाकर ढीली जुड़ी चीजों को गिराना—और यहाँ unused packages का tree से “हिलकर गिरना”—अर्थ में साफ था
source code diagram को physical object की तरह कल्पना करें, तो हिलाने पर root से reachable न होने वाली चीजें टूटकर गिर जाती हैं
यह reachability analysis से बहुत अलग नहीं है; बस एक expression spatial reasoning को थोड़ा ज्यादा जगाता है
कुछ code entry point नाम के trunk से जुड़ा नहीं है
tree shaking unconnected parts, यानी ढीली पत्तियों और dead branches को हटाता है
अगर किसी language की compiler toolchain network transfer के हिसाब से कुछ KB से भी कम की उपयोगी Wasm बना सके, तो नई संभावनाएं खुलती हैं
बहुत छोटी binaries Wasm के नए use cases खोलेंगी, और WasmGC निश्चित रूप से मदद करता है
Java और Kotlin भी आज 2–3KB के आसपास काफी अच्छा कर सकते हैं: https://developer.chrome.com/blog/wasmgc, https://twitter.com/bashorov/status/1661377260274720770
हालांकि कौन-सी API इस्तेमाल करते हैं, उसके हिसाब से बड़ा code साथ आ सकता है, इसलिए सावधानी चाहिए
फिर भी WasmGC की वजह से इन languages को memory management code के कई KB bundle में डालने की जरूरत नहीं पड़ती, इसलिए code size के मामले में ये पहले से ही C++ और Rust से काफी बेहतर हैं
JavaScript objects संभालते समय यह मदद कर सकता है, और कम efficient alternative memory allocator के तौर पर भी इस्तेमाल हो सकता है
फिर भी Wasm में default Rust allocator ज्यादातर मामलों में ठीक रहने की संभावना है
size optimization शुरू करके
wasm-optइस्तेमाल करें और Brotli से compress करें, तो download के हिसाब से 100KB से कम में बहुत भारी मात्रा में code रखा जा सकता हैWasm 100KB की cost की तुलना bundled JavaScript 100KB से सीधे करना गलती है, क्योंकि JavaScript की parsing और initialization कई गुना धीमी होती है
download time वास्तविक cost है, लेकिन first screen render time के लिए 100KB Wasm, 100KB JavaScript से काफी बेहतर है
फिर भी जितना छोटा हो उतना अच्छा, और Java, Kotlin, C#, Python, Go आदि का web applications में practical languages बनना उम्मीद जगाता है
यह भी जिज्ञासा है कि real application size कितना होगा
सबसे बड़ा फर्क framework design से आएगा लगता है, और virtual DOM diffing हमेशा Svelte, SolidJS, Rust के Leptos जैसी reactive component libraries से ज्यादा complex और धीमी ही होगी
WasmGC हर जगह supported हो जाए, तो performance पर language की तुलना में web framework choice का असर कहीं ज्यादा बड़ा होगा
“Wasm गैर-JavaScript भाषाओं में DOM programming को सोचने लायक बनाता है” — क्या यह बात सच में सही है, इस पर संदेह है
मेरी समझ में, Rust जैसी भाषाओं में DOM manipulation करने के लिए आखिरकार ऐसे bindings चाहिए होते हैं जो JavaScript की तरफ execute होने वाली calls को serialize करें
Wasm के मौजूदा रूप में यह अब भी JavaScript से बंधा हुआ ही नहीं है क्या, ऐसा लगता है
सैद्धांतिक रूप से अब runtime में DOM functions import करके और DOM object references से call करके JavaScript को bypass करते हुए runtime को सीधे call किया जा सकता है
असल में यह संभव है या नहीं, पक्का नहीं जानता, लेकिन GC कम-से-कम उस बिंदु तक पहुँचने के लिए जरूरी prerequisite mechanism देता है
मूल रूप से SolidJS जैसा है
#[component] fn App() -> impl IntoView { let (count, set_count) = create_signal(0); view! { "Click me: "{move || count()} } }JavaScript की तुलना में Wasm size overhead है, लेकिन गंभीर नहीं
wasm-optऔर Brotli compression के बाद इस counter app का Wasm bundle 37KB था, React जैसी range में, और execute होने के बाद काफी तेज़मैंने direct DOM manipulation नहीं आज़माया, लेकिन सामान्य components के लिए अच्छा लगता है
उदाहरण के लिए, Rust में implemented DOM को Rust में लिखे Wasm module से, JavaScript execute किए बिना, इस्तेमाल करने वाला रूप
हालांकि DOM API के कुछ हिस्से JavaScript semantics के हिसाब से specified हैं, इसलिए यह पेचीदा है; इसी वजह से पहले HTTP requests, TCP sockets, filesystem access जैसी JavaScript विरासत से कम जुड़ी चीज़ों पर काम हो रहा लगता है
“dead code elimination” शब्द तो बहुत पहले से था, फिर tree shaking जैसा expression क्यों आया, यह जानने की उत्सुकता है
“tree shaking” उन पूरे modules और functions को हटाने वाली whole-program analysis को कहा जाता है जिन्हें call नहीं किया जाता
conceptually दोनों एक जैसे हैं, लेकिन compiler writers को आम तौर पर इन्हें अलग से implement करना पड़ता है, इसलिए दो नाम होना मददगार है
tree shaking कल्पना करना आसान है, “dead code elimination” से बोलने में सरल और ज्यादा approachable है, इसलिए लगता है कि यह ज्यादा popular term बन गया
असल में कौन-सा term पहले आया, यह खोजा तो “dead-code elimination” का सबसे शुरुआती use 1973 के एक paper में मिला: https://research-repository.st-andrews.ac.uk/bitstream/handle/10023/22636/NicholasAlexandrakisMScThesis1973_original_C.pdf?sequence=1
Google Scholar में computing field में “tree shaking” या “tree shaker” के uses नहीं मिले; ज़्यादातर citrus trees जैसे पेड़ों से जुड़ी बातें थीं
जो सबसे शुरुआती discussion लगता है, वह comp.lang.lisp की एक post थी: https://groups.google.com/forum/#!topic/comp.lang.lisp/pspFr1XByZk
tree shaking का metaphor शायद कुछ fruit trees की harvesting method से आया होगा
पेड़ को हिलाने पर पके फल गिर जाते हैं
हालांकि fruit harvesting में जो गिरता है वही चाहिए होता है, जबकि serialized image store करते समय जो गिरता है उसे फेंक दिया जाता है, इसलिए यह बहुत अच्छा metaphor नहीं है
फल ज्यादा delicate होते हैं, इसलिए बहुत दूर गिरें तो उनमें चोट लग सकती है; आम तौर पर उन्हें हाथ से तोड़ा जाता है
shaking की process काफी rough होती है, लेकिन पेड़ को नुकसान नहीं पहुँचाती
मेरा दृष्टिकोण यह है कि JavaScript में algorithms और design simplification से size और performance को मेहनत से optimize किया जाए, ताकि brute-force computation की जरूरत वाले code के लिए bundle size और CPU की गुंजाइश बनाई जा सके
ClubCompy project में local storage के ऊपर FAT filesystem implement करने के लिए Wasm का इस्तेमाल हो रहा है, और यह computationally काफी महंगा निकला
इस साल बाद में pixel-perfect sprite collision detection को फिर से जोड़ते समय भी Wasm इस्तेमाल करने की योजना है
पहला implementation pure JavaScript में था, और जब स्क्रीन पर 256 sprites आपस में collide हुए तो framerate 1fps से नीचे गिर गया
मुझे लगता है कि इसे worker thread में लगभग मुफ्त में process करके performance impact के बिना बनाया जा सकेगा
ज्यादातर 2D games rectangular collision boxes इस्तेमाल करते हैं, इसकी वजह है कि player के लिए collision होगा या नहीं, यह predict करना आसान होता है
Pixel-level collision में वही movement भी animation cycle के phase के हिसाब से कभी collide कर सकता है और कभी नहीं
जो move पहले हमेशा काम करता था, अगर animation timing खराब तरह से match होने की वजह से fail हो जाए, तो अच्छा नहीं लगता
इसके अलावा pixel-level हमेशा realistic भी नहीं होता, क्योंकि sprite की छोटी details कपड़े या बाल जैसी चीजें हो सकती हैं जो असल में hard collision पैदा नहीं करतीं
अक्सर sprites हर frame में कई pixels move करते हैं, इसलिए individual pixel collision से उनके एक-दूसरे के आर-पार निकल जाने की संभावना बढ़ जाती है
सरल और predictable collision detection आम तौर पर सबसे अच्छा होता है
1fps ऐसा framerate है जो करीब 20 हजार collisions पर आना चाहिए
Pixel-level के लिए, memory headroom पर्याप्त हो तो एक सरल तरीका है: पूरे arena को offscreen canvas पर render करें, हर sprite को अलग रंग के stencil के रूप में draw करें, और फिर उसे inspect करें
यह linear time है और अलग partitioning की जरूरत भी नहीं
हालांकि anti-fingerprinting measures canvas data के lower bits को noise में बदल सकते हैं, इसलिए upper bits इस्तेमाल करने पड़ सकते हैं
यह लेख सही बात कह रहा है। Wasm में code size की समस्या है
ब्राउज़र में यह समस्या है क्योंकि साइट शुरू होने से पहले सारा code डाउनलोड करना पड़ता है, और serverless architecture में भी यह समस्या है क्योंकि client के इंतज़ार करने के दौरान code cold storage से ज़रूरत पड़ने पर किसी खास server पर load होता है
tree shaking मदद कर सकती है, लेकिन लगता है कि यह सिर्फ incremental optimization तक सीमित रहेगी
मूल रूप से Wasm program के bulky होने की वजह यह है कि हर भाषा का runtime और standard library पूरा साथ लाना पड़ता है
इसके उलट JavaScript में implementation और basic libraries ब्राउज़र उपलब्ध कराता है
यह सोचना आसान है कि ब्राउज़र सभी language runtimes पहले से लेकर नहीं चल सकता, लेकिन एक दूसरा approach भी सोचना चाहिए: shared libraries और dynamic linking
WebAssembly dynamic linking को support करता है, और कई Wasm modules को एक साथ load करके वे एक-दूसरे को call कर सकते हैं
लेकिन कई Wasm toolchains इसे support नहीं करना चाहतीं, और इस तरह design की गई हैं कि पूरा program और language runtime एक ही विशाल module में statically link हो जाए
Pyodide(CPython on Wasm) एक counterexample है; अभी इसे dynamic linking को ध्यान में रखकर design किया गया है
Cloudflare Workers हाल ही में Python को first-class support के रूप में जोड़ पाया, इसकी वजह भी यही है: https://blog.cloudflare.com/python-workers
Workers platform के पूरे technical lead के नज़रिये से देखें, तो एक ही machine पर चलने वाले सभी Workers compiled Pyodide runtime की एक ही copy share करते हैं, इसलिए हर Worker के लिए इसे अलग से load करने की ज़रूरत नहीं होती
अगर dynamic linking को अधिक व्यापक support मिले, तो ऐसी संरचना सोची जा सकती है जिसमें ब्राउज़र लोकप्रिय language runtimes, और आगे चलकर लोकप्रिय libraries, को पहले से load रखे और जिन web pages को उस runtime की ज़रूरत हो वे read-only code की वही copy share करें
ये runtimes फिर भी sandbox के अंदर चलते रहेंगे, इसलिए ब्राउज़र को उन पर भरोसा करने की ज़रूरत नहीं, बस उन्हें उपलब्ध कराना होगा
इससे browser maintainers को language implementation को पूरी तरह verify करने या उसके बारे में ज्यादा चिंता किए बिना JavaScript के अलावा भाषाओं के लिए “built-in” support वाला browser बनाया जा सकता है
मुझे बहुत जानकारी नहीं है, लेकिन मेरी समझ है कि वह तरीका ठीक से काम नहीं कर पाया
library versions बहुत ज़्यादा थे, इसलिए अलग-अलग versions वास्तव में व्यापक रूप से इस्तेमाल नहीं हुए, और बाद में privacy concerns की वजह से browsers cache को site या origin के हिसाब से अलग करने की दिशा में चले गए
हो सकता है कि यह caching को ध्यान में रखकर न हो, लेकिन यह technical problem से ज्यादा social problem के करीब एक कठिन समस्या है
अगर अधिक language runtimes को browser default support में डाल दिया जाए, तो नए browser के लिए entry barrier और बढ़ जाएगा, और लोगों को चाहिए होने वाली सभी libraries और runtimes को support करना भी संभव नहीं होगा
अगर हर कोई अपना runtime लाए और cache पर निर्भर रहे, तो सवाल बचता है कि JavaScript library caching में पहले झेली गई समस्याओं से कैसे बचेंगे
उदाहरण के लिए Go shared library में runtime और standard library के वे core हिस्से रखे जा सकते हैं जिन्हें कई programs इस्तेमाल करते हैं
app के अंदर dynamic library support न भी हो, तब भी सभी Go programs का size घटाया जा सकता है, और language runtime को space optimization बहुत ज्यादा करने की ज़रूरत नहीं रहेगी
क्योंकि वह पहले से loaded है और अगर कोई program एक भी function इस्तेमाल करता है, तो वह wasted space नहीं है
इससे उस language के programs के size optimization का cost model बदल जाएगा
included standard library functions language इस्तेमाल करते ही practically free हो जाते हैं, इसलिए उन्हें बस इस्तेमाल किया जा सकता है
हालांकि common libraries और frameworks में भी समस्या दोहराती है
Cloudflare पर चलाते समय Go के लिए Cloudflare standard library भी share करना चाहेंगे
समस्या यह है कि language runtime जितनी ही गति से evolve नहीं करती
कई language versions का support सीमित हो सकता है, या shared libraries समय के साथ जमा होती जाएँ और apps के बीच sharing का फायदा घट सकता है
JavaScript के पास “कोई विकल्प नहीं” वाला version model है, और वह मजबूत backward compatibility तथा कभी-कभी polyfill की मांग करता है
यह दूसरी भाषाओं के लिए कम उपयुक्त हो सकता है
अगर runtime सच में space घटाना चाहता है, तो plugin variety को सीमित कर सकता है
शिकायतें थीं, लेकिन “JavaScript इस्तेमाल करनी ही होगी” वाला तरीका browser में काफी अच्छी तरह काम किया
WebAssembly-आधारित भाषाओं को शायद बहुत ज्यादा विविध होने की ज़रूरत नहीं है, और Babel tower जैसी स्थिति में diversity की कीमत चुकानी पड़ती है
असल production जानकारी के आधार पर tree shaking करने से sophisticated static analysis algorithm लागू किए बिना भी बहुत-सा dead code या लगभग dead code काटा जा सकता है
खासकर Hoot जैसे context में
appendChildजैसी चीज़ें Scheme के अंदर call की जाने वाली external functions हैं: https://spritely.institute/news/building-interactive-web-pages-with-guile-hoot.htmltheoretical तौर पर किसी भी Wasm environment में JavaScript standard library के बड़े हिस्से को इस तरह इस्तेमाल किया जा सकता है
Zig इस तरह के उपयोग के लिए perfect है
निजी तौर पर मुझे लगता है कि अगर Wasm file size 100KB से कम है तो वह महत्वपूर्ण factor नहीं है, और MB से ऊपर जाए तो महत्वपूर्ण हो जाता है
built-in GC कुछ apps के लिए महत्वपूर्ण है, लेकिन सभी के लिए नहीं, और web apps को GC के बिना बनाना सबसे अच्छा है
Wasm का इस्तेमाल करने वाली app के सफल होने में सबसे महत्वपूर्ण factor अभी भी performance advantage ही है
मैं Cloudflare Pages पर एक Blazor ऐप चला रहा हूँ; डाउनलोड तेज़ है और performance भी अच्छी है, लेकिन load time बेहद खराब है
मुझे लगता है कि .NET से इसे हल करना संभव नहीं है, और मुख्य समस्या यह लगती है कि object-oriented languages में design के स्तर पर सब कुछ आपस में उलझा रहता है
साथ ही JavaScript में लगाए गए पैसे के पैमाने से मुकाबला करना मुश्किल है, और JavaScript में copy/paste मानो language feature जैसा है, इसलिए यह cheat code जैसा लगता है
तीसरी बात, Blazor में भी अब भी JavaScript और उस तरफ़ की expertise चाहिए, और मुझे यही मुख्य समस्या लगती है
समस्या यह है कि उस दौर की languages कई use cases में reflection पर निर्भर थीं
.NET के बड़े हिस्सों से ऐसे use cases हटाने और unused code removal के लिए safe के रूप में mark करने पर लोग काफ़ी मेहनत कर रहे हैं
अगर reflection से method call होने की संभावना हो, तो यह जानना मुश्किल है कि क्या safely remove किया जा सकता है
भले ही
Foo.Bar()को call करने वाली कोई जगह न दिखे, अगर कोईReflection.getClass(someClass).runMethod(someVar)कर रहा हो और वे variables"Foo"और"Bar"पर set हों, तो आप क्या करेंगेउदाहरण के लिए Dart precompiled apps में reflection की अनुमति नहीं देता, जिससे unused code को safely remove किया जा सकता है: https://docs.flutter.dev/resources/faq#does-flutter-come-with-a-reflection-mirrors-system
Dart object-oriented language है, लेकिन उसने runtime code generation और runtime reflection से बचकर compile-time code generation चुना है
.NET भी इसी दिशा में जा रहा है, लेकिन यह रातों-रात होने वाला काम नहीं है
हालांकि, जैसा कि दूसरों ने कहा है, non-JavaScript languages के साथ यह समस्या भी है कि browser के JavaScript runtime में शामिल standard library features में से जो हिस्से वे इस्तेमाल करती हैं, उन्हें खुद साथ में ship करना पड़ता है
मौजूदा packaging model Mono के ऊपर Wasm package होने की सीमाओं के कारण .NET trimming क्षमताओं का ठीक से फायदा नहीं उठा पाता
असल में कितना अच्छा shrink किया जा सकता है, यह देखने के लिए AOT के साथ कोई सामान्य application build करना बेहतर है, तब छोटा binary मिलता है
dotnet/runtimelabमें NativeAOT-LLVM Wasm target के experimental support से कहीं छोटा bundle size और कहीं बेहतर performance मिलती है, लेकिन यह अभीdotnet/runtimeमें नहीं बल्किdotnet/runtimelabके तहत है, इसलिए यह कब उपलब्ध होगा, पता नहीं