23 पॉइंट द्वारा GN⁺ 2025-06-08 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • C/C++ कोड को Emscripten से WebAssembly में पोर्ट करके ब्राउज़र में चलने वाला वेबऐप बनाने की प्रक्रिया को एक वास्तविक Rubik’s Cube solver उदाहरण के आधार पर विस्तार से समझाया गया है
  • Hello World से लेकर multithreading, callback, persistent storage, modularization तक, browser/WebAssembly environment में आने वाली ठोस चुनौतियों और उनके समाधान को चरणबद्ध तरीके से कवर किया गया है
  • JavaScript asynchronous initialization, function export, Web Worker और Spectre issues, IDBFS के जरिए IndexedDB persistent storage जैसी चीज़ों पर आधारित व्यावहारिक troubleshooting पर फोकस है
  • बार-बार इस बात पर ज़ोर दिया गया है कि Emscripten का abstraction अक्सर वास्तव में 'leaky abstractions' साबित होता है, इसलिए web platform की सीमाओं और उसकी आंतरिक संरचना को समझना ज़रूरी है
  • frontend के न्यूनतम JavaScript/HTML ज्ञान के साथ भी जटिल C codebase को web पर ले जाने के वास्तविक अनुभव के आधार पर, यह मौजूदा C/C++ libraries को web में port करना चाहने वाले developers के लिए उपयोगी टिप्स और व्यावहारिक know-how देने वाली अनुभव-आधारित गाइड है

परिचय

  • हाल ही में Rubik’s Cube optimal solution algorithm को web app के रूप में लागू करने वाला एक प्रोजेक्ट किया गया
  • C में विकसित Rubik’s Cube optimization solver को Emscripten से compile करके WebAssembly के रूप में web browser में चलाने की प्रक्रिया को दर्ज किया गया है
  • WebAssembly का मुख्य उपयोग इसलिए है क्योंकि इससे JavaScript की तुलना में लगभग native के करीब performance web पर हासिल की जा सकती है
  • यह लेख पारंपरिक web development tutorial नहीं, बल्कि मौजूदा C/C++ code को web पर port करना चाहने वाले developers के लिए एक ‘कष्टदायक यात्रा’ है
  • अगर web development का अनुभव कम भी हो, तो HTML, JavaScript की बुनियादी संरचना और browser developer tools का उपयोग जानकर इसे फॉलो किया जा सकता है

environment setup

  • सभी example code git repository, github में उपलब्ध हैं
  • Emscripten की installation ज़रूरी है (installation method के लिए official site देखें), और web server के लिए darkhttpd या Python http.server आदि का उपयोग किया जा सकता है
  • tutorial code examples को Linux और UNIX family systems पर test किया गया है। Windows users के लिए WSL(Windows Subsystem for Linux) की सिफारिश है

Hello World

  • C code के Hello World को emcc -o index.html hello.c command से compile करने पर index.html(web page), index.wasm(WebAssembly bytecode), index.js(JavaScript glue code) ये तीन files बनती हैं
  • यह browser और Node.js दोनों में चल सकता है, और दोनों environments में उपयोग का तरीका अलग होता है
  • केवल .wasm बनाना हो तो -sSTANDALONE_WASM option का उपयोग करें
  • Emscripten में केवल .wasm generate करना संभव है, लेकिन अधिकतर मामलों में JavaScript glue code आवश्यक होता है

Intermezzo I: WebAssembly क्या है?

  • WebAssembly(WASM) web browser के भीतर चलने वाली high-performance virtual machine के लिए एक low-level language है
  • WASM को 2017 के बाद से सभी प्रमुख browsers support करते हैं
  • मूल रूप से Emscripten, C/C++ code को asm.js नामक JavaScript subset में बदलता था, लेकिन WASM आने के बाद यह बदल गया
  • इसका text representation भी मौजूद है और यह stack-based structure रखता है। हाल तक यह केवल 32-bit architecture support करता था, इसलिए 4GB से अधिक memory का उपयोग नहीं हो पाता था, लेकिन WASM64 अब धीरे-धीरे browsers में आ रहा है

library build

  • C function multiply() को WASM में build करने और फिर JavaScript से call करने का एक बुनियादी उदाहरण दिया गया है
  • default build में Emscripten function name के आगे underscore(_) जोड़ देता है (उदाहरण: _multiply)
  • function को बाहर expose करने के लिए -sEXPORTED_FUNCTIONS option देना ज़रूरी है
  • library load करते समय initialization asynchronous होती है, इसलिए onRuntimeInitialized या await जैसी async handling की ज़रूरत पड़ती है
  • practice code repository के 01_library folder में है

Intermezzo II: JavaScript और DOM

  • JavaScript से HTML के elements तक पहुँचने और उन्हें बदलने के लिए Document Object Model(DOM) का उपयोग करना पड़ता है
  • event listener(addEventListener), built-in operators/functions आदि की मदद से dynamic UI बनाया जा सकता है
  • उदाहरण के लिए input, button और result display वाले basic HTML/JavaScript integration structure को समझाया गया है
  • script को अलग/मर्ज करने के व्यावहारिक तरीके और उनसे जुड़े issues (जैसे defer का उपयोग, DOM element load order) भी बताए गए हैं

library modularization और loading

  • WASM library को कई बार include करने या Node.js और web दोनों में reuse करने के लिए इसे MODULARIZE, EXPORT_NAME options के साथ module form में build किया जा सकता है
  • Node.js compatibility के लिए .mjs (ES6 module) extension recommended है
  • web/Node दोनों में import MyLibrary from ... जैसे module usage संभव हैं

multithreading

  • WebAssembly में performance बढ़ाने के लिए pthreads-based multithreaded code को port किया जा सकता है
  • function के अंदर कई threads बनाकर parallel calculation tasks (जैसे prime numbers की गिनती) चलाई जाती हैं
  • build के समय -pthread, -sPTHREAD_POOL_SIZE= options की ज़रूरत होती है
  • वास्तविक browser में Cross-Origin-Opener-Policy: same-origin, Cross-Origin-Embedder-Policy: require-corp जैसे HTTP headers भी जोड़ने पड़ते हैं
  • सभी examples repository के 03_threads folder में उपलब्ध हैं

Intermezzo III: Web Workers और Spectre

  • Emscripten multithreading, Web Workers के जरिए implement होती है (Web Workers अलग process होते हैं और message-based communication structure का उपयोग करते हैं)
  • shared memory(SharedArrayBuffer) के उपयोग पर security constraints मौजूद हैं
  • 2018 में Spectre vulnerability सामने आने के बाद cross-origin isolated requirement और उससे जुड़े headers अनिवार्य हो गए

main thread blocking से सावधान

  • यदि लंबे tasks browser के main UI thread को BLOCK कर दें, तो user experience बहुत खराब हो जाता है
  • इससे बचने के लिए web worker(Worker) का उपयोग किया जाता है: UI/input handling और computation को स्पष्ट रूप से अलग किया जाता है
  • postMessage, onmessage से main-worker के बीच event-based communication implement किया जाता है
  • web worker के भीतर Emscripten-WASM module लोड करके केवल asynchronous computation को संभाला जाता है

callback functions

  • C function के parameter के रूप में function pointer(callback) पास करने पर, JavaScript के function object के साथ automatic integration संभव नहीं होती
  • Emscripten द्वारा दिए गए addFunction(), UTF8ToString() आदि का उपयोग करना पड़ता है, और build के समय -sEXPORTED_RUNTIME_METHODS, -sALLOW_TABLE_GROWTH options भी जोड़ने होते हैं
  • callback को स्थिर रूप से काम करने के लिए केवल main thread पर ही call किया जाना चाहिए (web worker में इस तक पहुँचना संभव नहीं)

persistent storage

  • user के browser में data को स्थायी रूप से store करने के लिए Emscripten का IDBFS(IndexedDB-based file system) इस्तेमाल किया जाता है
  • build के समय --lidbfs.js flag और **--pre-js ** आदि से initial setup करना पड़ता है
  • C code में file I/O functions(fopen, fread, fwrite) को वैसे ही इस्तेमाल किया जा सकता है, लेकिन data reflect/sync करने की वास्तविक प्रक्रिया के लिए JavaScript में explicit mapping और sync handling ज़रूरी है
  • browser की sandbox/security policy के कारण local file system तक सीधी पहुँच केवल Node.js में संभव है; browser में सुरक्षित persistent storage के लिए IDBFS जैसे backend का उपयोग करना पड़ता है

निष्कर्ष

  • इस tutorial की पूरी प्रक्रिया से यह विस्तार से सीखा जा सकता है कि जटिल native C/C++ code को न्यूनतम JavaScript और HTML के साथ भी सुरक्षित रूप से और बिना performance loss के browser में कैसे चलाया जाए
  • वास्तविक environment में multithreading, callback, async handling, storage integration जैसे सभी मुख्य tracks की चुनौतियों और उनके समाधान का अनुभव मिलता है, साथ ही संबंधित settings और browser constraints जैसे आधुनिक trends भी समझ में आते हैं
  • दिए गए Git repository examples को देखकर इन्हें अपने प्रोजेक्ट में लागू और आगे विस्तारित किया जा सकता है

1 टिप्पणियां

 
GN⁺ 2025-06-08
Hacker News राय
  • काश लोग इस बात पर ध्यान देते कि सिर्फ़ एक्सटेंशन .js से .mjs में बदला गया है; असलियत यह है कि चाहे जो एक्सटेंशन इस्तेमाल करो, किसी न किसी समस्या से टकराना तय है। dojo से लेकर CommonJS, AMD, ESM, webpack, esbuild, rollup तक तरह-तरह के module systems इस्तेमाल कर चुके व्यक्ति के नज़रिए से, इस बात से 100% सहमति महसूस होती है।
    • commonjs से esm में जाना लगभग python2 से python3 में माइग्रेशन जैसा बहुत बड़ा बदलाव था, लेकिन उम्मीद के मुकाबले फ़ायदे कम और झंझट ज़्यादा लगे। अब बहुत सी libraries सिर्फ़ esm को support करती हैं, इसलिए आजकल npm के versions tab में पिछले एक महीने में सबसे ज़्यादा डाउनलोड हुए version को चुनना पड़ता है, क्योंकि वही आख़िरी commonjs version होने की अच्छी संभावना होती है। यक़ीनन esm को ज़्यादा उन्नत module system कहा जा सकता है, लेकिन tc39 ने इसे commonjs के साथ लगभग जानबूझकर incompatible बनाया है—जैसे top-level await वगैरह—यह बात सच में समझ से बाहर है।
    • JS में modules का इतिहास सचमुच किसी trauma जैसा लगता है। अब browser में import maps भी आ गए हैं, तो आगे कौन-सी नई "मज़ेदार" समस्याएँ पैदा होंगी, यह देखने की उत्सुकता है।
    • हाल ही में पता चला कि Function object runtime पर कोई भी JS code compile कर सकता है, इसलिए मेरे उस environment में जहाँ import भी इस्तेमाल नहीं कर सकता, यह किसी lifesaver की तरह बहुत काम आ रहा है। JS ecosystem में शायद इसकी ज़रूरत कम हो, लेकिन मेरे लिए यह काफ़ी मददगार है।
    • इसलिए सबको bun.sh इस्तेमाल करना चाहिए—ऐसा दावा।
    • सवाल कि क्या .esm.js भी इस्तेमाल नहीं किया जा सकता?
  • अगर इस लेख में उन हिस्सों की ओर और इशारा करना हो जो लंबे समय में समस्या पैदा कर सकते हैं, तो var keyword की जगह let या const इस्तेमाल करने की सलाह दी जाएगी। var अभी भी काम करता है, लेकिन आजकल ज़्यादातर JS developers linter से var के इस्तेमाल पर रोक लगा देते हैं। var सिर्फ़ function scope देता है, इसलिए दूसरी भाषाओं से आने वाले ज़्यादातर developers कभी न कभी इससे भ्रमित होते ही हैं। native app porting issue के रूप में एक उदाहरण दिया गया कि compile time पर Ctrl-C, Ctrl-V को hardcode करके copy/paste लागू कर दिया जाता है, इसलिए Linux और Windows पर तो चल जाता है, लेकिन Mac पर नहीं। web में इसे copy, paste events detect करके handle करना चाहिए, और Unity जैसे frameworks में भी hardcoded keys की वजह से Mac पर copy-paste न चलने की चीज़ देखी गई है। ज़्यादातर games में इसकी ज़रूरत नहीं होती, लेकिन जब copy-paste चाहिए वाला feature web पर भेजते हैं तो यह ज़रूर समस्या बनता है।
  • web/NodeJS में multithreading से बहुत चिढ़ होने की शिकायत; अफ़सोस इस बात का कि mutex या rwlock जैसे synchronization primitives से values को contexts के बीच (जैसे v8 isolates) ट्रांसफ़र करने लायक बनाने के बजाय, व्यवहार में लगभग बेकार SharedArrayBuffer ही दिया गया। threads के बीच synchronization आख़िरकार thunking और data copy को RPC layer के ज़रिए करने वाली संरचना बन जाती है। हमारी company का production app 70~100GB RAM इस्तेमाल करने वाला बहुत बड़ा app है (मैंने बनने से पहले से ऐसा था), इसलिए हम native code के आधार पर memory pages और custom data structures को सीधे manage करते हुए serialization/deserialization को न्यूनतम रखने वाला एक अजीब-सा समाधान ढूँढ रहे हैं; v8 में string encoding utf16 होने की वजह से native layer में JS values handle करना महँगा पड़ता है।
    • सवाल कि 100GB RAM इस्तेमाल करने वाला यह app सच में web app ही होना चाहिए था क्या? यह ज़्यादा किसी internal tool जैसा लगता है, जिसे C# जैसी भाषा में लिखना चाहिए था।
  • यह ecosystem इतना chaotic है कि इसे "masochist" कहना भी अब कुछ ज़्यादा सामान्य-सा लगता है।
    • एक पंक्ति कि शायद chaos तो इसमें पहले से ही निहित है।
  • लेख खुद बहुत अच्छी तरह लिखा गया है, और उससे भी बढ़कर यह देखकर हैरानी हुई कि शुरुआत इतनी कठिन और जटिल राह से की गई। project setup ही सबसे मुश्किल हिस्सा होने का एहसास होता है। security/header समस्या से तुरंत टकराने पर शाबाशी, हालांकि अक्सर अनुमानित समस्या CORS ही होती है। हमारी company भी emscripten/C++ से build कर रही है और आगे WebGPU/shaders तथा WebAudio तक जोड़ना है, इसलिए यात्रा और भी कठिन दिख रही है।
  • पहले browser में code compile होना "धीमा होगा" ऐसा एक धुँधला-सा अनुमान था, लेकिन OP ने अच्छे से समझाया कि ऐसा ज़रूरी नहीं है। Emscripten project भी इस बात पर ज़ोर देता है कि "LLVM, Emscripten, Binaryen, WebAssembly के संयोजन की वजह से output छोटा रहता है और लगभग native जैसी speed पर चलता है" (emscripten.org)
    • आज मेरे लिए कुछ "yellow car syndrome" जैसा दिन रहा। पिछले हफ़्ते तक मुझे Emscripten के बारे में पता भी नहीं था, फिर project में SDL जोड़ते समय CMake में APPLE, MSVC, EMSCRIPTEN targets वाला comment दिखा, और आज ही HN पर फिर Emscripten की चर्चा दिख गई। अब लगता है कि सच में समय निकालकर इसे गहराई से समझने का समय आ गया है।
    • "लगभग native speed" जैसी अभिव्यक्ति काफ़ी subjective लगती है; असल में यह कितना तेज़ है, इसके लिए docs में कोई संख्यात्मक data नहीं मिला।
  • लेख उपयोगी लगा, और खुद भी C में लिखा एक compiler WebAssembly में compile करके web playground बनाना चाहते हैं। साथ ही, आधुनिक browsers JavaScript के ज़रिए SQLite इस्तेमाल कर सकते हैं—तो क्या wasm में भी यह संभव है, यह जिज्ञासा है। अगर emscripten, C code की sqlite API calls को browser SQLite DB से bridge कर दे, तो वह बहुत आदर्श होगा, इसलिए और खोजबीन करने लायक बात है।
  • जिज्ञासा कि SSL के लिए port 48 क्यों इस्तेमाल किया गया—क्या इसके पीछे कोई खास वजह थी?
    • जवाब कि यह H48 नाम से लिया गया एक random port था। इस web app को अतिरिक्त HTTP headers चाहिए थे, इसलिए पूरी site को प्रभावित किए बिना इसे लागू करने के लिए बस अलग port इस्तेमाल किया गया। यह https://h48.tronto.net पर redirect भी हो जाता है, और आगे चलकर OpenBSD के httpd और relayd setup को और बेहतर करने या इसे पूरी तरह अलग domain पर ले जाने पर भी विचार किया जा रहा है।