WebAssembly में चलने वाला Fortran
(gws.phd)- पुराने scientific और engineering computation assets, यानी Fortran numerical code को browser में लाने के लिए WebAssembly compile path की ज़रूरत होती है, और webR इसके लिए patched LLVM
flang-newका उपयोग करता है f2c, LFortran, Dragonegg, Classic Flang, LLVM Flang — इन सभी में modern Fortran support, target architecture, maintainability के मामले में सीमाएँ हैं, इसलिए अभी तक कोई सरल standard solution मौजूद नहीं है- LLVM Flang सीधे
wasm32-unknown-emscriptentarget को support नहीं करता, इसलिएTargetWasm32जोड़ना पड़ता है, और runtime linking में host और target केlongsize के अंतर से समस्या पैदा होती है - patched
flang-new, Emscripten, और Fortran runtime static libraries को मिलाकर Fortran subroutine को C या JavaScript से call किया जा सकता है, औरPRINT,ALLOCATE,CHARACTERarguments भी handle किए जा सकते हैं - BLAS 3.12.0 और LAPACK 3.12.0 reference implementation को WebAssembly static libraries के रूप में build करके handwritten digit classification और polynomial interpolation demo को browser में चलाया गया
मौजूदा Fortran numerical code को browser में लाना
- Fortran 1957 में आया एक पुराना language है, लेकिन scientific और engineering computation में लंबे समय से इस्तेमाल होता रहा है, और modern Fortran अब Fortran 77 के fixed-format constraints से काफी हद तक बाहर आ चुका है
- लक्ष्य यह है कि modern Fortran routines को WebAssembly में compile करके browser में चलाया जाए, numerical arguments लिए जाएँ, BLAS·LAPACK routines से computation किया जाए, और फिर result लौटाया जाए या console में print किया जाए
- यह approach SciPy या R जैसे BLAS·LAPACK पर निर्भर higher-level programming environments को web पर लाने में मदद कर सकती है
- JavaScript या Rust में numerical routines को दोबारा लिखने के बजाय, पहले से validate किए गए Fortran-आधारित tools और libraries का उपयोग किया जा सकता है
- webR project patched LLVM
flang-newcompiler के साथ Fortran code को WebAssembly में compile करता है - मौजूदा तरीका hacks पर निर्भर है, और अधिक अनुभवी compiler developers की मदद के बिना इन बदलावों को LLVM में योगदान करना कठिन है
Fortran→WebAssembly tools की वर्तमान स्थिति
- 2024 तक कई tools और toolchains मौजूद हैं, लेकिन पूरी तरह functional और simple solution अभी भी उपलब्ध नहीं है
-
f2cf2cFortran 77 को C code में बदलता है, और फिर Emscripten उसे WebAssembly में compile कर सकता है- Pyodide Fortran code वाले Python packages को compile करते समय यही तरीका उपयोग करता है
- Pyodide roadmap में भी इस तरीके को “अच्छा काम नहीं करता” कहा गया है
- यह modern Fortran code के लिए उपयुक्त नहीं है, और conversion के बाद भी गंभीर errors और बड़े पैमाने पर patches की ज़रूरत पड़ती है
-
LFortran
- LFortran ने पिछले कुछ वर्षों में काफ़ी प्रगति की है और सीधे WebAssembly में compile कर सकता है
- लेकिन यह अभी alpha stage में है, इसलिए developers के अनुसार वास्तविक code compile issues की उम्मीद की जानी चाहिए
- MINPACK जैसे कुछ projects compile हो जाते हैं, लेकिन पूरी Fortran spec support न होने से बड़े projects fail हो सकते हैं
- इसका development goal Fortran 2018 का full support है, और Jupyter जैसा interactive Fortran REPL इसकी खास feature है
-
Dragonegg
- Dragonegg एक GCC plugin है जो GCC frontend का उपयोग करके LLVM IR export करता है
- LLVM backend से WebAssembly output बनाया जा सकता है, और webR ने शुरू में Fortran source को WebAssembly में compile करने के लिए यही तरीका अपनाया था
- लेकिन इसका latest supported version
gcc-4.8औरllvm-3.3है, इसलिए बहुत पुराने GCC·LLVM की ज़रूरत पड़ती है - ज़्यादातर users को VM या Docker container चाहिए होता है, और Dragonegg द्वारा export किया गया LLVM IR भी WebAssembly output के लिए अतिरिक्त post-processing माँगता है
- 2020 में Fortran code को WebAssembly में compile करने का यह लगभग इकलौता व्यावहारिक तरीका था
-
Classic Flang
- Classic Flang, open source किए गए PGI/NVIDIA
pgfortranपर आधारित LLVM-target Fortran compiler है - यह 32-bit output support नहीं करता, इसलिए
wasm32target पर इस्तेमाल नहीं किया जा सकता - लिखे जाने के समय Firefox, Chrome, और Node
wasm64को support करते हैं, लेकिन feature flag के पीछे - project documentation भी संकेत देती है कि नए projects के लिए Classic Flang चुनना शायद अच्छा विचार नहीं है
- Classic Flang, open source किए गए PGI/NVIDIA
-
LLVM Flang
- LLVM Flang LLVM के लिए Fortran frontend को शुरू से दोबारा लागू करने वाला project है, और LLVM 11 से LLVM project का हिस्सा है
- इसे अभी production-ready नहीं माना जाता, लेकिन
flang-newका pre-production version वास्तविक Fortran code compile करने के लिए काफ़ी उपयोगी हो चुका है - default state में यह WebAssembly output नहीं बना सकता
- LLVM की modular design का उपयोग करके Flang frontend और LLVM WebAssembly backend को साथ जोड़ा जा सकता है
- 2020 में भी यह संभव था, लेकिन उसके लिए बड़े LLVM patches, custom math routines injection, और multi-stage compile process की ज़रूरत थी
- अब
flang-newfrontend के विकास की वजह से LLVM source में छोटे बदलावों के साथ Fortran→WebAssembly compiler बनाया जा सकता है
WebAssembly के लिए LLVM Flang build करना
- package manager से install किए गए LLVM में
flang-newbinary शामिल न हो, ऐसा हो सकता है- उदाहरण के लिए macOS Homebrew के LLVM v17.0.6 में
flang-newcommand नहीं मिला
- उदाहरण के लिए macOS Homebrew के LLVM v17.0.6 में
- क्योंकि LLVM Flang source में बदलाव करने थे, इसलिए LLVM v18.1.1 source को सीधे build किया गया
- CMake configuration में default target triple को
wasm32-unknown-emscriptenरखा गया, औरWebAssemblytarget तथाclang;flang;mlirprojects enable किए गए - build पूरा होने के बाद
build/bin/flang-new --versionमें यह जानकारी दिखाई देती है- version:
flang-new version 18.1.1 - target:
wasm32-unknown-emscripten - thread model:
posix
- version:
पहली समस्या: wasm32 target implement नहीं है
- साधारण Fortran subroutine
foo.f08कोflang-newसे compile करने पर यह error मिलता हैnot yet implemented: target not implemented
- वजह यह है कि
flang-newमेंwasm32-unknown-emscriptentarget triple अभी implement नहीं किया गया है - इसका समाधान
flang/lib/Optimizer/CodeGen/Target.cppमेंTargetWasm32target characteristics जोड़ने वाला patch है- default width को 32 पर सेट किया जाता है
- complex arguments और return types को WebAssembly-side LLVM types में बदलने के लिए marshalling define किया जाता है
llvm::Triple::ArchType::wasm32branch मेंTargetWasm32को जोड़ा जाता है
- patch के बाद दोबारा build करने पर Fortran source WebAssembly object में compile हो जाता है
file foo.oresult:WebAssembly (wasm) binary module version 0x1 (MVP)llvm-nm foo.oमेंfoo_symbol देखा जा सकता है
C और JavaScript से Fortran subroutine call करना
- Fortran routines आम तौर पर arguments को reference से pass करती हैं, और
INTENT()से argument के उपयोग का तरीका घोषित किया जा सकता है - उदाहरण Fortran subroutine
foo,x,y,zinteger arguments लेकरz = x + yकरती है - native build में
gfortran -c foo.f08 -o foo.oचलाने पर symbol name जैसेfoo_के अंत में underscore लग सकता है - C से call करते समय external symbol को
extern void foo_(int*, int*, int*);की तरह declare करके arguments के addresses pass किए जाते हैं - WebAssembly में
flang-newसे बनेfoo.oको Emscripten से C code के साथ link किया जा सकता हैemcc main.c foo.o -o main.jsnode main.jsoutput:1 + 1 = 2
-
JavaScript से direct call
- C code के बिना JavaScript से Fortran subroutine को सीधे call भी किया जा सकता है
- Emscripten link stage में
_foo_,_malloc,_freeexport किए जाते हैं emcc foo.o -sEXPORTED_FUNCTIONS=_foo_,_malloc,_free -o foo.js- JavaScript
Module._malloc()से integers के लिए storage allocate करता है,Module.HEAPU32में values लिखता है, फिरModule._foo_(x, y, z)call करता है - execution result इस प्रकार है
x = 123y = 456x + y = 579- browser में
foo.jsऔरstandalone.jsको HTML में load करके यही result JavaScript console में देखा जा सकता है
दूसरी समस्या: Fortran runtime library
PRINT *, "Hello, World!"वाली Fortran subroutine build करने पर link stage में runtime symbols न मिलने की error आती है_FortranAioBeginExternalListOutput_FortranAioOutputAscii_FortranAioEndIoStatement
- कारण यह है कि LLVM Fortran runtime library अभी WebAssembly के लिए compile नहीं की गई थी
- runtime library LLVM source tree के
llvm-project/flang/runtimeमें C++ में लिखी गई है - Emscripten के
em++औरemarसे runtime source build करने पर static librarybuild/flang/runtime/libFortranRuntime.aबनाई जा सकती है - इस library को link करने पर
Hello, World!build आगे बढ़ती है, लेकिन शुरुआत में function signature mismatch warning आती है
तीसरी समस्या: host और target के long size का अंतर
hello.oऔर Fortran runtime library को link करने पर_FortranAioOutputAsciisignature mismatch warning आती है- Fortran object side
(i32, i32, i64) -> i32expect करती है - Emscripten से built runtime side
(i32, i32, i32) -> i32के रूप में define होती है
- Fortran object side
- WebAssembly में अलग-अलग compilation units में define किए गए symbols के arguments और return types का consistent होना ज़रूरी है
- यह समस्या सिर्फ warning पर खत्म नहीं होती, बल्कि Node में run करने पर
RuntimeError: unreachableके साथ fail होती है - LLVM Flang के
RTBuilder.hमें एक TODO comment है किsizeofका उपयोगbuild == host == targetमान लेता है - modern 64-bit Unix-like hosts में
sizeof(long)8 bytes होता है, लेकिनwasm32-unknown-emscriptentarget में यह 4 bytes होना चाहिए - Fortran के
CHARACTERtype arguments को function या subroutine में pass करते समय string length के लिए एक hidden argument जुड़ सकता है - Fortran runtime library में यह length argument
size_tके रूप में declare है, जोtypedefchain के बादunsigned longके बराबर बनता है - इसी hidden argument के size difference से
i64औरi32mismatch पैदा होता है
अस्थायी patch: 4-byte value force करना
- आदर्श समाधान यह है कि cross-compilation के समय
flang-new, host से स्वतंत्र होकर target architecture और data model के अनुसारi32याi64emit करे - फिलहाल
wasm32और Emscripten के हिसाब सेlongsize को 4 bytes पर hardcode करने वाला patch उपयोग किया गया - patch दो हिस्सों में है
RTBuilder.hमेंlongऔरunsigned longके model types को8 * sizeof(...)के बजाय8 * 4पर force किया गयाCodeGen.cppमेंmalloc()call argument को 64-bit के बजाय 32-bit integer के रूप में generate किया गया, और allocation size कोi32में cast किया गया
- इस बदलाव से Fortran 90 में आए
ALLOCATE()-आधारित dynamic allocation भी ठीक हो गया - rebuild के बाद
hello.f08औरhello.cको runtime library के साथ link करने पर warning के बिना build हो जाता है, और Node में यह output मिलता हैHello, World!
BLAS को WebAssembly में build करना
- BLAS linear algebra के common operations, जैसे matrix·vector multiplication, करने वाले low-level routines का set है
- मूल BLAS routines 1979 में जारी हुए थे, और numerical computing में de facto standard हैं
- reference implementation BLAS 3.12.0 Fortran 90 में लिखी गई है और netlib से प्राप्त की जा सकती है
make.incमें ये tools specify करके build किया जाता हैFC = ../build/bin/flang-newFFLAGS = -O2AR = emarRANLIB = emranlib
- build result के रूप में
blas_LINUX.astatic library बनती है - उदाहरण Fortran routine
bar, BLAS level 2 routineZGEMV()को call करती है ZGEMV()complex matrix-vector operation करती है, और example मेंCOMPLEX(KIND=8)arguments तथाCHARACTERconfig argument'N'इस्तेमाल होता है- C program में complex arrays बनाकर Fortran routine को pass किया जाता है, फिर result print किया जाता है
- Node execution result इस प्रकार है
Y[0]: 23.000000 + 6.000000iY[1]: 18.000000 + 10.000000iY[2]: 6.000000 + 16.000000i
- इससे पुष्टि होती है कि Fortran 90 source से compile किया गया BLAS, WebAssembly के तहत चल रहा है
browser example: handwritten digit classifier
- demo एक multilayer perceptron (MLP) artificial neural network के जरिए हाथ से लिखे 0–9 digits को classify करता है
- user mouse या touchscreen से digit draw कर सकता है, और network द्वारा predict की गई relative probabilities दाईं ओर के plot में दिखाई जाती हैं
- model weights Python में pre-train किए गए थे, लेकिन classification run time पर browser में JavaScript और WebAssembly से की जाती है
- MLP classification प्रक्रिया मूलतः matrix-vector addition और multiplication की repeated sequence है
- भारी computation एक Fortran subroutine करती है, जो BLAS level 2 routine
DGEMV()का उपयोग करती है
LAPACK को WebAssembly में build करना
- LAPACK linear algebra problems को numerically solve करने वाली software library है, जो BLAS के ऊपर बनी है
- reference implementation LAPACK 3.12.0 netlib पर उपलब्ध है और modified BSD license के तहत वितरित होती है
make.inc.exampleकोmake.incमें copy करने के बाद ये settings बदली जाती हैंFCको builtflang-newके full path पर सेट किया जाता हैFFLAGS = -O2AR = emarRANLIB = emranlibTIMER = INT_CPU_TIME
make libcommand से WebAssembly static libraryliblapack.aबनाई जाती है- उसके बाद LAPACK routines को BLAS example जैसी ही पद्धति से call किया जा सकता है
browser example: linear algebra से polynomial interpolation
- demo दिए गए points के set के लिए interpolating polynomial ढूँढता है, और दिखाता है कि LAPACK routines browser में चल सकती हैं
- जब user plot पर click करके नया point जोड़ता है, तो सभी points से गुजरने वाला interpolating polynomial निकाला जाता है
- इसके लिए Vandermonde’s method इस्तेमाल किया जाता है
- इस तरह प्राप्त linear algebra equation को LAPACK की
DGELS()routine से numerically solve किया जाता है ndata points को ठीक-ठीक शामिल करने वाला degreen-1polynomial हमेशा पाया जा सकता हैnबड़ा होने पर लगातार data points के बीच polynomial में बड़ा oscillation आने वाला Runge phenomenon हो सकता है, जिसे spline interpolation से टाला जा सकता है
बाकी चुनौतियाँ और उपलब्ध tools
- patched LLVM Flang के साथ modern Fortran code को WebAssembly object में compile किया जा सकता है
- इस approach का फायदा यह है कि web के लिए numerical algorithms को JavaScript में दोबारा लिखने के बजाय, पहले से मौजूद Fortran tools और libraries का उपयोग किया जा सकता है
- अगर
flang-newआधिकारिक रूप से WebAssembly support करे, तो webR और R packages के लिए LLVM fork बनाए रखने का बोझ कम होगा - फिलहाल LLVM Flang और cross-compilation issues को सभी targets पर सही ढंग से हल करने के लिए बेहतर रास्ते की ज़रूरत है
- जो users LLVM Flang को खुद build करना कठिन मानते हैं या करना नहीं चाहते, उनके लिए GitHub Container Registry पर patched LLVM Flang binary Docker container उपलब्ध है
1 टिप्पणियां
Hacker News की राय
थोड़ा बैकग्राउंड जोड़ें तो, यह Fortran exploration George के उस शानदार WebR काम का हिस्सा है जो वे R को ब्राउज़र में चलाने के लिए कर रहे हैं
R source में काफी Fortran code है, और मेरी समझ से WebR पहले f2c से Fortran को C में compile करता था, फिर उस C को wasm में compile करता था
LLVM Flang patch की वजह से WebR को असली Fortran compiler के साथ build करना संभव हो गया
George ने blog post में सीधे नहीं कहा, लेकिन उन्होंने कहा था कि वे चाहते हैं कि Flang इस patch को स्वीकार करे या इसे किसी बेहतर तरीके से implement करे
ऐसा होने पर अलग patch maintain करने की जरूरत नहीं रहेगी, और बिना modification वाला Flang wasm में compile कर सकेगा, जिससे Fortran इस्तेमाल करने वाले दूसरे projects को भी फायदा होगा
https://docs.r-wasm.org/webr/latest/
हालांकि मैं Nvidia के Fortran product development को पूरा करने के लिए जरूरी कामों पर focus कर रहा हूं, इसलिए इस तरह के काम के लिए मेरे पास समय नहीं बचता
20 साल पहले Xilinx में FORTRAN compile करने का काम किया था, और मुझे बस इतना याद है कि f2c.h header file में
barfकी definition थी/* f2c.h -- Standard Fortran to C header file //* barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed."(https://www.netlib.org/clapack/f2c.h)
समझाने के तरीके के रूप में सबसे सरल non-trivial example इस्तेमाल करना सच में अच्छा है
लेख “JavaScript से BLAS function call करना” जैसी concrete समस्या पर आधारित है, इसलिए बहुत कुछ सीखने जैसा लगा, और यह बेहतरीन लेख है
[1] https://en.m.wikipedia.org/wiki/The_Theoretical_Minimum
समझ नहीं आ रहा कि प्रभावित होना चाहिए या डर जाना चाहिए, शायद दोनों ही
f18 build करते समय llvm-project/main का latest source इस्तेमाल करने की सलाह दूंगा
project तेजी से बदल रहा है, इसलिए पहले से ठीक हो चुकी समस्याओं को debug करना या पहले से implement हो चुके features miss करना समय की बर्बादी है
क्या मकसद WebAssembly port पर काम करके intermediate code को उस point तक ले जाना है जहां वह Fortran तक काम करे?
WebAssembly development के बारे में ज्यादा नहीं जानता
consumer के नजरिए से WebAssembly अभी क्या देता है? या यह उस भविष्य के लिए foundational work जैसा है जहां programs सच में portable हो जाएंगे?
मैंने सुना है कि WebAssembly device network या files जैसे access restrictions को आसान बना देता है, लेकिन नहीं पता यह सिर्फ theory है या पहले से implement हो चुका है
मुख्य फर्क यह है कि Wasm में खुद कोई standard library नहीं है और यह input/output functions expose नहीं करता
इसलिए host, यानी VM आप खुद बना सकते हैं और Wasm binary के इस्तेमाल के लिए functions expose कर सकते हैं; Wasm binary बाहरी दुनिया तक सिर्फ उन्हीं functions के जरिए पहुंच सकती है
एक और फायदा यह है कि binary format proprietary नहीं है और इसकी specification है, इसलिए कोई भी Wasm VM implement कर सकता है
हालांकि अभी इसे अच्छी स्थिति में कहना मुश्किल है; यह बहुत early है, W3C जैसे groups में कई नए features standardize हो रहे हैं और प्रक्रिया भी बहुत धीमी है
ज्यादातर targets पर deploy या cross-compile करने का तरीका भी है
यह वैसा ही है जैसे आम तौर पर हमें नहीं पता होता और खास फर्क नहीं पड़ता कि computer CPU ARM है या x86
इसलिए अगर आपको इस तरह की details में रुचि नहीं है कि यह native code के रूप में चल रहा है या JVM·.NET·WASM जैसी VM पर, तो यह कहना मुश्किल है कि यह दूसरी solutions से ज्यादा क्या देता है
आम तौर पर सिर्फ खराब cases दिखते हैं, और वही “हर Electron program फूला हुआ और resources खाने वाला monster है, और हर native app अपने-आप efficient software engineering का चमत्कार है” जैसे memes में बदल जाता है
काश मैंने 1981/82 में लिखा अपना Fortran 78 code संभालकर रखा होता, तो यहां चलाकर test कर सकता था
वह Jovial programming language source code formatter था, और Fortran में करने लायक काम नहीं था, लेकिन उस समय वही एकमात्र विकल्प था
क्या JavaScript में इस्तेमाल करने लायक, कुछ हद तक production-ready linear algebra ecosystem है?
खोजने पर ज्यादातर करीब 10 साल पुराने परिचित libraries के JavaScript ports मिलते हैं, जैसे emscripten के जरिए किए गए ports; सोच रहा हूं कि कहीं मैं कुछ miss तो नहीं कर रहा
LFortran को ज़्यादा गहराई से न कवर करना अजीब है
इसका एक शानदार और हैरान कर देने वाला WASM example भी है जो online चलता है
https://dev.lfortran.org/
2020 में इसमें बहुत-सी features मौजूद नहीं थीं और यह Fortran के बहुत छोटे subset को ही support करता था, लेकिन अब यह कहीं ज़्यादा व्यापक language features support करता है और काफ़ी Fortran code compile कर सकता है
यह WebAssembly में भी native compile कर सकता है
हालांकि LFortran इस्तेमाल करने में अभी भी कुछ rough edges हैं, और developers का कहना है कि project फिलहाल alpha stage में माना जाता है और वास्तविक code compile करते समय समस्याएँ आ सकती हैं
MINPACK जैसे कुछ projects को सफलतापूर्वक compile किया जा सकता है, लेकिन क्योंकि यह अभी Fortran की पूरी specification support नहीं करता, इसलिए कई बड़े projects अब भी compile नहीं हो पाते
LFortran developers का लक्ष्य Fortran 2018 का पूरा support देना है, और इसकी एक notable feature Jupyter जैसा interactive Fortran REPL है
कुछ और वर्षों के development के बाद, WebAssembly के लिए Fortran code compile करने का यह एक बेहतरीन विकल्प बन सकता है
साथ ही https://dev.lfortran.org पर LFortran demo देखने को कहा गया है, जो बहुत प्रभावशाली है, लेकिन मैंने सबसे पहले जो
x * 2कोx * 3में बदलने की कोशिश की, उसे current code generator support नहीं करता था.NET और Java के ऊपर Fortran भी मौजूद है
https://www.silverfrost.com/14/ftn95/ftn95_fortran_95_for_microsoft_dotnet_features.aspx
https://dl.acm.org/doi/10.1145/376656.376833
https://medium.com/@tomasreimers/compiling-tensorflow-for-the-browser-f3387b8e1e1c पर काम करते समय मैंने सोचा था कि यह वाकई अच्छी बात है कि TensorFlow ने Fortran में लिखी लोकप्रिय math libraries BLAS/Lapack के बजाय Eigen का इस्तेमाल किया
वरना काम कहीं ज़्यादा बढ़ जाता