2 पॉइंट द्वारा GN⁺ 2024-07-31 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • 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 टिप्पणियां

 
GN⁺ 2024-07-31
Hacker News की राय
  • Oliver, जो Porffor के मुख्य डेवलपर हैं, ने घोषणा की कि वे Porffor पर full-time काम करेंगे: https://x.com/canadahonk/status/1818347311417938237

    • कहा गया है कि GitHub के co-founder और पूर्व CEO defunkt एक अभी सार्वजनिक न किए गए भविष्य के प्रोजेक्ट के लिए funding दे रहे हैं
      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 और Float types रखकर, ज़रूरत पड़ने पर Number में downcast करते हुए, उन्हें registers में भी रखा जा सकता है
    मुख्य समस्या यह है कि TS और V8 दोनों तेज़ी से बदलने वाले non-standard targets हैं। ऐसे प्रोजेक्ट के लिए बड़ी team चाहिए, और compatibility बनाए रखना अपने-आप में अलग काम बन जाता है

    • अतिरिक्त extensions के बिना TypeScript उम्मीद से कम मददगार है, क्योंकि इसे शुरू से इस उद्देश्य के लिए design नहीं किया गया था
      एक सरल उदाहरण के तौर पर 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 से खराब हो सकती है
    • Porffor contributor के तौर पर मैं सहमत नहीं हूँ। JavaScript में भी compile time पर सुधार की काफी गुंजाइश है
      JS के लिए static type analysis tools बनाने पर काफी काम हुआ है, और बहुत thorough analysis भी संभव है। दिमाग में आने वाला एक उदाहरण, हालांकि थोड़ा पुराना है, TAJS है
    • इस idea से कुछ हद तक जुड़ा एक project AssemblyScript है: https://www.assemblyscript.org
    • ECMAScript 4 भाषा में बेहतर types जोड़ने की कोशिश थी, लेकिन काफी पहले अफसोसजनक रूप से असफल हो गई
      अच्छा होगा अगर TypeScript में भी integer जैसा type specify किया जा सके। सामान्य TS→JS compilation में भले ही const val: int को const val: number जैसा ही माना जाए, लेकिन TS-aware आधुनिक runtimes उस अतिरिक्त जानकारी का उपयोग कर सकते होंगे
      सोचता हूँ कि const counter: Number जैसी syntax स्वीकार की जा सकती है या नहीं
    • “मैंने इसके बारे में सोचा, लेकिन बेहतर performance पाना मुश्किल है” कहने के बाद, आप उस approach की बात कर रहे हैं जो site के पहले screen के ऊपर ही समझाई गई चीज़ों की मांग करती है
      पता नहीं 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 को मात देना मुश्किल है

    • डेवलपर के तौर पर सहमत हूँ। यह Porffor द्वारा संभावित रूप से मदद की जा सकने वाली एक दिलचस्प use case लगती है। कभी इस पर बात करना अच्छा होगा
  • यह देखना अच्छा है कि ज्यादा 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

    • संदर्भ के लिए, Static Hermes JS को WASM में compile करने की capability को पूरी तरह support करता है। मौजूदा LLVM backend होने की वजह से यह लगभग मुफ्त में मिल जाने वाली capability है। उदाहरण के लिए https://x.com/tmikov/status/1706138872412074204 देखें
      हालांकि यह हमारा focus नहीं है; हम मुख्य रूप से React Native पर focus कर रहे हैं। उस environment में WASM का खास मतलब नहीं है
      Static Hermes की सबसे अहम capability runtime soundness guarantee करने वाला type checker है। Porffor बहुत दिलचस्प है, मैं इसे कुछ समय से देख रहा हूं और इसकी सफलता की कामना करता हूं
    • Porffor contributor के तौर पर, मुझे यह अच्छी तुलना लगती है। हालांकि Porffor भी technically promises support करता है। बस यह synchronously काम करता है
      Kiesel जैसा approach है: https://kiesel.dev/
    • कुछ छोटी corrections हैं। Porffor अभी पूरी तरह self-hosted नहीं है, लेकिन उम्मीद है कि यह संभव होगा। हालांकि Array.prototype.filter, Math.sin, atob जैसी built-in capabilities आंशिक रूप से खुद को compile करती हैं
      हाल ही में Porffor ने भी basic async/promise/await support करना शुरू किया है। अभी यह बहुत अच्छा नहीं है
    • ऐसा लग रहा है जैसे LLVM पर depend करने को मैंने बुरा बता दिया हो
  • JavaScript का एक subset है जिसे आसानी से compile किया जा सकता है, और मुश्किल हिस्सा उसके बाहर की लंबी tail है। फिर भी यह research होना बढ़िया है कि वह boundary कहां है और उस subset से कितना फायदा मिल सकता है

  • मुझे सच में अच्छा लगा कि यह String.blink support करता है। developers में humour और playfulness होना हमेशा अच्छा sign है

    • अगर लक्ष्य “ECMAScript host का web browser जैसा behave करना” है, तो इसे support करना ही चाहिए। क्योंकि यह specification का हिस्सा है: https://tc39.es/ecma262/multipage/additional-ecmascript-feat...
      Implementation भी function() { return "" + this + ""; } जितना trivial है, इसलिए ECMAScript host web browser न भी हो तो भी इसे implement करना बनता है। ऐसे मामलों में यह optional है। मुझे उम्मीद नहीं है कि इसका “humour या playfulness” से कोई संबंध है
    • String.blink test262 में शामिल है, इसलिए project goal हासिल करने के लिए इसे practically support करना ही होगा
  • मैं सोच रहा हूं कि वह कौन-सा subtle फर्क है जो मुझसे छूट गया। समझ नहीं आ रहा कि “ahead-of-time JS engine” “JS-to-Wasm compiler” से बेहतर description क्यों है। अगर यह मुख्य रूप से framing strategy है, तो वह भी ठीक है

    • पहले से ऐसे projects हैं जो JS interpreter bundle करके JS-to-WASM करते हैं। इसलिए यह expression शायद उस तरीके से फर्क को ज्यादा साफ करने के लिए है
  • यहां बताया गया 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...

    • शायद intention यह है कि Test262 regression पैदा करने वाला काम temporary हो और अलग branch में किया जाए, फिर regression हटाने के लिए जरूरी fixes समेत पूरी हालत में ही main में merge किया जाए। नया version number उस merge के बाद ही इस्तेमाल किया जा सकता है
  • Welsh में इसका मतलब “बैंगनी” है

    • इसकी etymology बैंगनी रंग के लिए Greek शब्द से आई है, और उसी root वाला English word शायद porphyry है, जो एक बैंगनी mineral है और सबसे आम उदाहरण है
  • अलग-अलग use cases के लिए कई JS engines आते देखना refreshing है
    applications में plugins embed करने के लिए, मैं llrt के जरिए quickjs में ज्यादा Node-compatible APIs जोड़ने पर काम कर रहा हूं
    https://github.com/awslabs/llrt