- Porffor एक research project है जो JavaScript को रनटाइम पर नहीं, बल्कि पहले से compile करके WebAssembly और native binary में बदलता है
- इंटरप्रेटर को साथ में bundle न करने की वजह से इसका लक्ष्य मौजूदा JS→Wasm projects की तुलना में output को 10~30 गुना छोटा और तेज़ बनाना है
- Native build में भी runtime को package नहीं किया जाता, इसलिए binary size अधिकतम 1000 गुना तक कम हो सकती है; उदाहरण में यह लगभग 90MB से घटकर 100KB से कम हो जाती है
- इसे JS में लिखा गया है, इसमें
evalनहीं है, और यह TypeScript को अलग build step के बिना native support देने वाली संरचना पर ज़ोर देता है - AOT static analysis optimization और execution से पहले compilation के लिए फ़ायदेमंद है, लेकिन
evalजैसी dynamic JS evaluation कठिन होती है, और यह अभी शुरुआती चरण में है जहाँ बहुत-सा JS अभी काम नहीं करता
Porffor किस तरह execution करता है
- Porffor एक research project है जो JavaScript को WebAssembly और native binary में Ahead-of-Time compile करता है
- Porffor से compile की गई TypeScript binary ही इस पेज को serve कर रही है
- इसे शुरू से ही AOT को ध्यान में रखकर लिखा गया है, इसलिए इसकी संरचना उन optimizations को आज़माने के लिए बनाई गई है जो पारंपरिक JS execution तरीकों में कठिन थे
WebAssembly और native output में अंतर
-
JS → Wasm
- Porffor का WebAssembly output मौजूदा JS→Wasm projects की तुलना में 10~30 गुना छोटा और तेज़ है
- मुख्य अंतर यह है कि यह JS को सीधे compile करता है और interpreter को bundle नहीं करता
- Wasm में JS चलाने से sandboxed execution संभव होता है, लेकिन इसमें performance का बड़ा नुकसान हो सकता है; Porffor का फोकस इस लागत को कम करने पर है
- उपयोग के संभावित उदाहरण:
- Server-side JS hosting: edge runtime में Wasm sandboxing के साथ बिना अत्यधिक isolation के सुरक्षित execution दिया जा सकता है
- AOT का कम overhead, JIT की तुलना में, उसी hardware पर कम performance loss के साथ अधिक customers चलाने की संभावना बनाता है
- Reverse engineering के प्रति अधिक प्रतिरोध: संवेदनशील JS के लिए compiled code, obfuscation की तुलना में reverse engineer करना अधिक कठिन हो सकता है
-
JS → Native
- Runtime को package किए बिना JS को वास्तव में compile किया जाता है, इसलिए binary size अधिकतम 1000 गुना तक छोटी हो सकती है
- उदाहरण size लगभग 90MB → 100KB से कम है
- अंदरूनी तौर पर यह JS को C में compile करता है और फिर native में compile करता है, इसलिए जहाँ C का उपयोग हो सकता है वहाँ JS का भी उपयोग हो सकता है
- उपयोग के संभावित उदाहरण:
- embedded systems, game consoles आदि में तेज़ JS execution
- छोटे JS CLI apps जो 1MB से कम के one-click executable में compile हो सकें
AOT के फायदे और सीमाएँ
- पारंपरिक interpreter या कई JIT चरणों को startup time और JS performance के बीच संतुलन बनाना पड़ता है
- AOT में पहले compile और बाद में execution होता है, इसलिए compile speed developer experience के लिए महत्वपूर्ण है, लेकिन user experience को प्रभावित नहीं करती
- यह तरीका C++ और Rust की तरह static analysis आधारित optimization करने की गुंजाइश देता है
- मुख्य कमी यह है कि
evalजैसी dynamic JS evaluation मौजूद नहीं है, और JS इंजन को नए सिरे से बनाना पड़ता है - यह अभी शुरुआती चरण में है, इसलिए बहुत-सा JS अभी काम नहीं करता, लेकिन सुधार का काम जारी है
- ECMAScript compatibility की प्रगति को ट्रैक करने के लिए हर commit पर आधिकारिक test suite Test262 चलाया जाता है
1 टिप्पणियां
Hacker News की राय
Oliver, जो Porffor के मुख्य डेवलपर हैं, ने घोषणा की कि वे Porffor पर full-time काम करेंगे: https://x.com/canadahonk/status/1818347311417938237
https://news.ycombinator.com/user?id=defunkt
मैंने भी कुछ ऐसा ही सोचा था, लेकिन JavaScript में कहीं बेहतर performance पाना मुश्किल लगता है। शायद सबसे अच्छा विकल्प JS को V8 C++ calls में transpile करने जैसा ही होगा
सच में शानदार optimization तब मिलती है जब TypeScript या उसके करीब की किसी चीज़ को compile किया जाए। types का उपयोग करने से बड़ा फायदा मिल सकता है, और जिन हिस्सों में types नहीं हैं वे मूल रूप से धीमे JS calls पर गिर जाएंगे। interfaces को virtual function tables या direct calls में घटाया जा सकता है, और maps के बजाय structs पर काम किया जा सकता है।
IntऔरFloattypes रखकर, ज़रूरत पड़ने परNumberमें downcast करते हुए, उन्हें registers में भी रखा जा सकता हैमुख्य समस्या यह है कि TS और V8 दोनों तेज़ी से बदलने वाले non-standard targets हैं। ऐसे प्रोजेक्ट के लिए बड़ी team चाहिए, और compatibility बनाए रखना अपने-आप में अलग काम बन जाता है
एक सरल उदाहरण के तौर पर TypeScript integers और floating-point numbers में फर्क नहीं करता; दोनों को number की तरह देखता है। इसलिए हर array access में type conversion चाहिए। अगर TypeScript static compilation में मदद के लिए design किया गया होता, तो शायद यह फर्क मौजूद होता
इससे भी बड़ी समस्या TypeScript की structural subtyping है। इस विशेषता की वजह से compiler के लिए function में pass किए गए non-primitive arguments की physical structure को static रूप से तय करना व्यावहारिक रूप से असंभव हो जाता है। JIT dynamic shape analysis कर सकता है, इसलिए हर field access में performance JIT से खराब हो सकती है
JS के लिए static type analysis tools बनाने पर काफी काम हुआ है, और बहुत thorough analysis भी संभव है। दिमाग में आने वाला एक उदाहरण, हालांकि थोड़ा पुराना है, TAJS है
अच्छा होगा अगर TypeScript में भी
integerजैसा type specify किया जा सके। सामान्य TS→JS compilation में भले हीconst val: intकोconst val: numberजैसा ही माना जाए, लेकिन TS-aware आधुनिक runtimes उस अतिरिक्त जानकारी का उपयोग कर सकते होंगेसोचता हूँ कि
const counter: Numberजैसी syntax स्वीकार की जा सकती है या नहींपता नहीं site बदल गई है, या मैं कुछ miss कर रहा हूँ
windmill.dev में, जब users code deploy करते हैं, तो Bun build का उपयोग करके scripts और सभी dependencies को एक single JS file में bundle किया जाता है, और उसे load करके cold start और memory usage सुधारा जाता है। bundle size की वजह से output S3 में store किया जाता है
अगर सब कुछ native में bundle किया जा सके, तो खेल पूरी तरह बदल जाएगा। Bun का cold start कितना भी अच्छा हो, छोटे binary से सीधे native execution को मात देना मुश्किल है
यह देखना अच्छा है कि ज्यादा JS runtimes Wasm तक कैसे पहुंच रहे हैं। यह project React Native projects की iOS/Android speed बढ़ाने के लिए Facebook के JS engine Static Hermes की याद दिलाता है
दोनों का लक्ष्य JS test262 compliance है, और Porffor native और Wasm दोनों output support करता है, जबकि Static Hermes अभी मुख्य रूप से native output पर focus करता है। Porffor pure JS में लिखा गया है और खुद को compile करने की दिशा में है, जबकि Static Hermes LLVM पर depend करता है। Porffor में async/promise/await support अभी limited था, और Static Hermes कुछ limitations के साथ इन्हें support करता है। Static Hermes C++ में, और Porffor मुख्य रूप से JS में लिखा गया है। दोनों TypeScript support करते हैं, लेकिन Static Hermes TS AST को Flow में transpile करता है, और Porffor इसे native तौर पर support करता है। Static Hermes के पास
evalजैसी compile करने में कठिन JS situations के लिए fallback interpreter है, और Porffor सिर्फ pre-compilation support करता हैकुल मिलाकर, उम्मीद है कि यह project traction पाएगा और edge के JavaScript engines को और तेज बना सकेगा। Wasmer के Syrus की तरफ से
https://github.com/facebook/hermes/discussions/1137
https://github.com/tc39/test262
https://wasmer.io
हालांकि यह हमारा focus नहीं है; हम मुख्य रूप से React Native पर focus कर रहे हैं। उस environment में WASM का खास मतलब नहीं है
Static Hermes की सबसे अहम capability runtime soundness guarantee करने वाला type checker है। Porffor बहुत दिलचस्प है, मैं इसे कुछ समय से देख रहा हूं और इसकी सफलता की कामना करता हूं
Kiesel जैसा approach है: https://kiesel.dev/
Array.prototype.filter,Math.sin,atobजैसी built-in capabilities आंशिक रूप से खुद को compile करती हैंहाल ही में Porffor ने भी basic async/promise/await support करना शुरू किया है। अभी यह बहुत अच्छा नहीं है
JavaScript का एक subset है जिसे आसानी से compile किया जा सकता है, और मुश्किल हिस्सा उसके बाहर की लंबी tail है। फिर भी यह research होना बढ़िया है कि वह boundary कहां है और उस subset से कितना फायदा मिल सकता है
मुझे सच में अच्छा लगा कि यह
String.blinksupport करता है। developers में humour और playfulness होना हमेशा अच्छा sign हैImplementation भी
function() { return "" + this + ""; }जितना trivial है, इसलिए ECMAScript host web browser न भी हो तो भी इसे implement करना बनता है। ऐसे मामलों में यह optional है। मुझे उम्मीद नहीं है कि इसका “humour या playfulness” से कोई संबंध हैString.blinktest262 में शामिल है, इसलिए project goal हासिल करने के लिए इसे practically support करना ही होगामैं सोच रहा हूं कि वह कौन-सा subtle फर्क है जो मुझसे छूट गया। समझ नहीं आ रहा कि “ahead-of-time JS engine” “JS-to-Wasm compiler” से बेहतर description क्यों है। अगर यह मुख्य रूप से framing strategy है, तो वह भी ठीक है
यहां बताया गया versioning scheme थोड़ा संदिग्ध लगता है
अगर किसी change की वजह से कुछ Test262 tests में regression आ जाए, तो version number भी वापस जा सकता है। यानी Porffor के पास monotonically increasing version numbers और किसी जरूरी change की Test262 regression पैदा करने की क्षमता, दोनों साथ-साथ नहीं हो सकते
https://github.com/CanadaHonk/porffor?tab=readme-ov-file#ver...
Welsh में इसका मतलब “बैंगनी” है
अलग-अलग use cases के लिए कई JS engines आते देखना refreshing है
applications में plugins embed करने के लिए, मैं llrt के जरिए quickjs में ज्यादा Node-compatible APIs जोड़ने पर काम कर रहा हूं
https://github.com/awslabs/llrt