Fortran yang Berjalan di WebAssembly
(gws.phd)- Untuk membawa kode numerik Fortran, aset komputasi sains dan rekayasa yang sudah lama ada, ke browser, diperlukan jalur kompilasi WebAssembly; webR menggunakan LLVM
flang-newyang telah dipatch f2c, LFortran, Dragonegg, Classic Flang, dan LLVM Flang masing-masing memiliki keterbatasan dalam dukungan Fortran modern, arsitektur target, dan kemudahan pemeliharaan, sehingga belum ada solusi standar yang sederhana- LLVM Flang tidak langsung mendukung target
wasm32-unknown-emscripten, sehingga perlu penambahanTargetWasm32; pada tahap linking runtime, perbedaan ukuranlongantara host dan target menimbulkan masalah - Dengan menggabungkan
flang-newyang telah dipatch, Emscripten, dan pustaka statis runtime Fortran, subroutine Fortran dapat dipanggil dari C atau JavaScript, serta dapat menanganiPRINT,ALLOCATE, dan argumenCHARACTER - Implementasi referensi BLAS 3.12.0 dan LAPACK 3.12.0 dibangun sebagai pustaka statis WebAssembly untuk menjalankan demo klasifikasi angka tulisan tangan dan interpolasi polinomial di browser
Membawa kode numerik Fortran lama ke browser
- Fortran adalah bahasa lama yang muncul pada 1957, tetapi telah lama digunakan dalam komputasi sains dan rekayasa; Fortran modern sebagian besar sudah lepas dari batasan format tetap Fortran 77
- Tujuannya adalah mengompilasi rutin Fortran modern ke WebAssembly agar berjalan di browser, menerima argumen numerik, menghitung rutin BLAS·LAPACK, lalu mengembalikan hasil atau mencetaknya ke konsol
- Pendekatan ini memungkinkan lingkungan pemrograman tingkat tinggi yang bergantung pada BLAS·LAPACK, seperti SciPy atau R, dibawa ke web
- Tanpa menulis ulang rutin numerik dalam JavaScript atau Rust, alat dan pustaka berbasis Fortran yang sudah teruji dapat dimanfaatkan
- Proyek webR mengompilasi kode Fortran ke WebAssembly dengan compiler LLVM
flang-newyang telah dipatch - Cara saat ini bergantung pada hack, dan tanpa bantuan developer compiler yang lebih berpengalaman, perubahan ini sulit dikontribusikan ke LLVM
Kondisi alat Fortran→WebAssembly
- Per 2024 ada beberapa alat dan toolchain, tetapi belum ada solusi sederhana dengan fitur lengkap
-
f2cf2cmengubah Fortran 77 menjadi kode C, sehingga Emscripten dapat mengompilasinya ke WebAssembly- Pyodide menggunakan cara ini saat mengompilasi paket Python yang berisi kode Fortran
- Dalam roadmap Pyodide pun, cara ini dinilai “tidak bekerja dengan baik”
- Tidak cocok untuk kode Fortran modern, dan setelah konversi pun tetap memerlukan patch luas serta perbaikan error fatal
-
LFortran
- LFortran dalam beberapa tahun terakhir mengalami peningkatan fitur yang besar dan dapat langsung mengompilasi ke WebAssembly
- Para developernya menyatakan bahwa karena masih berada pada tahap alfa, masalah kompilasi kode nyata masih dapat diperkirakan
- Beberapa proyek seperti MINPACK dapat dikompilasi, tetapi karena belum mendukung seluruh spesifikasi Fortran, proyek yang lebih besar bisa gagal
- Target pengembangannya adalah dukungan penuh Fortran 2018, dan fitur menonjolnya adalah REPL Fortran interaktif yang mirip Jupyter
-
Dragonegg
- Dragonegg adalah plugin GCC yang menggunakan frontend GCC untuk mengeluarkan LLVM IR
- Dengan backend LLVM, ia dapat menghasilkan output WebAssembly, dan ini adalah cara yang pertama kali digunakan webR untuk mengompilasi sumber Fortran ke WebAssembly
- Versi dukungan terbarunya adalah
gcc-4.8danllvm-3.3, sehingga membutuhkan GCC·LLVM yang sangat lama - Sebagian besar pengguna memerlukan VM atau container Docker, dan LLVM IR yang dikeluarkan Dragonegg juga harus melalui post-processing tambahan agar menghasilkan output WebAssembly
- Pada 2020, secara praktis ini hampir menjadi satu-satunya cara untuk mengompilasi kode Fortran ke WebAssembly
-
Classic Flang
- Classic Flang adalah compiler Fortran bertarget LLVM yang berbasis PGI/NVIDIA
pgfortranyang telah dijadikan open source - Karena tidak mendukung output 32-bit, ia tidak dapat digunakan untuk target
wasm32 - Firefox, Chrome, dan Node pada saat penulisan mendukung
wasm64, tetapi masih terkunci di balik feature flag - Dokumentasi proyeknya juga menyatakan bahwa memilih Classic Flang untuk proyek baru mungkin bukan ide yang baik
- Classic Flang adalah compiler Fortran bertarget LLVM yang berbasis PGI/NVIDIA
-
LLVM Flang
- LLVM Flang adalah proyek yang mengimplementasikan ulang frontend Fortran untuk LLVM dari awal, dan sejak LLVM 11 telah masuk ke proyek LLVM
- Meski belum dianggap siap produksi, versi praproduksi
flang-newsudah cukup bisa digunakan untuk mengompilasi kode Fortran nyata - Dalam kondisi default, ia belum dapat menghasilkan output WebAssembly
- Dengan memanfaatkan desain modular LLVM, frontend Flang dan backend WebAssembly LLVM dapat digunakan bersama
- Pada 2020 hal ini juga sudah mungkin, tetapi memerlukan patch LLVM yang lebih besar, injeksi rutin matematika kustom, dan proses kompilasi bertahap
- Kini, berkat pengembangan frontend
flang-new, compiler Fortran→WebAssembly dapat dibuat hanya dengan perubahan kecil pada sumber LLVM
Membangun LLVM Flang untuk WebAssembly
- LLVM yang dipasang melalui package manager mungkin tidak menyertakan binary
flang-new- Sebagai contoh, pada LLVM v17.0.6 dari macOS Homebrew, perintah
flang-newtidak ditemukan
- Sebagai contoh, pada LLVM v17.0.6 dari macOS Homebrew, perintah
- Karena sumber LLVM Flang harus dimodifikasi, sumber LLVM v18.1.1 dibangun secara langsung
- Konfigurasi CMake menetapkan triple target default ke
wasm32-unknown-emscripten, serta mengaktifkan targetWebAssemblydan proyekclang;flang;mlir - Setelah build selesai, informasi berikut dapat dikonfirmasi dari
build/bin/flang-new --version- Versi:
flang-new version 18.1.1 - Target:
wasm32-unknown-emscripten - Model thread:
posix
- Versi:
Masalah pertama: target wasm32 belum diimplementasikan
- Jika subroutine Fortran sederhana
foo.f08dikompilasi denganflang-new, muncul error berikutnot yet implemented: target not implemented
- Penyebabnya adalah triple target
wasm32-unknown-emscriptenbelum diimplementasikan diflang-new - Solusinya adalah patch yang menambahkan karakteristik target
TargetWasm32keflang/lib/Optimizer/CodeGen/Target.cpp- Menetapkan lebar default ke 32
- Mendefinisikan marshalling untuk mengubah argumen bilangan kompleks dan tipe return ke tipe LLVM sisi WebAssembly
- Menghubungkan
TargetWasm32pada cabangllvm::Triple::ArchType::wasm32
- Setelah patch dan build ulang, sumber Fortran dikompilasi menjadi objek WebAssembly
- Hasil
file foo.o:WebAssembly (wasm) binary module version 0x1 (MVP) - Simbol
foo_dapat dikonfirmasi dillvm-nm foo.o
- Hasil
Memanggil subroutine Fortran dari C dan JavaScript
- Rutin Fortran umumnya meneruskan argumen lewat referensi, dan
INTENT()dapat digunakan untuk mendeklarasikan cara argumen dipakai - Contoh subroutine Fortran
foomenerima argumen integerx,y,zdan menjalankanz = x + y - Pada build native, jika menjalankan
gfortran -c foo.f08 -o foo.o, nama simbol dapat diberi garis bawah di belakang sepertifoo_ - Saat memanggil dari C, simbol eksternal dideklarasikan seperti
extern void foo_(int*, int*, int*);, lalu argumen diteruskan sebagai alamat - Di WebAssembly,
foo.oyang dibuat denganflang-newdapat di-link dengan kode C menggunakan Emscriptenemcc main.c foo.o -o main.js- Output
node main.js:1 + 1 = 2
-
Pemanggilan langsung dari JavaScript
- Subroutine Fortran juga dapat dipanggil langsung dari JavaScript tanpa kode C
- Pada tahap linking Emscripten,
_foo_,_malloc, dan_freediekspor emcc foo.o -sEXPORTED_FUNCTIONS=_foo_,_malloc,_free -o foo.js- JavaScript mengalokasikan ruang penyimpanan integer dengan
Module._malloc(), menulis nilai keModule.HEAPU32, lalu memanggilModule._foo_(x, y, z) - Hasil eksekusinya sebagai berikut
x = 123y = 456x + y = 579- Di browser,
foo.jsdanstandalone.jsdapat dimuat dari HTML untuk melihat hasil yang sama di konsol JavaScript
Masalah kedua: pustaka runtime Fortran
- Jika subroutine Fortran yang berisi
PRINT *, "Hello, World!"dibangun, pada tahap linking muncul error karena simbol runtime tidak ada_FortranAioBeginExternalListOutput_FortranAioOutputAscii_FortranAioEndIoStatement
- Penyebabnya adalah pustaka runtime LLVM Fortran belum dikompilasi untuk WebAssembly
- Pustaka runtime ditulis dalam C++ di
llvm-project/flang/runtimepada source tree LLVM - Dengan membangun sumber runtime memakai
em++danemardari Emscripten, pustaka statisbuild/flang/runtime/libFortranRuntime.adapat dibuat - Jika pustaka ini di-link, build
Hello, World!berjalan, tetapi pada awalnya muncul peringatan ketidakcocokan signature fungsi
Masalah ketiga: perbedaan ukuran long antara host dan target
- Saat
hello.odi-link dengan pustaka runtime Fortran, muncul peringatan ketidakcocokan signature_FortranAioOutputAscii- Sisi objek Fortran mengharapkan
(i32, i32, i64) -> i32 - Sisi runtime yang dibangun dengan Emscripten didefinisikan sebagai
(i32, i32, i32) -> i32
- Sisi objek Fortran mengharapkan
- WebAssembly mengharuskan tipe argumen dan return simbol yang didefinisikan di berbagai unit kompilasi konsisten
- Masalah ini tidak berhenti sebagai peringatan, tetapi gagal saat dijalankan di Node dengan
RuntimeError: unreachable - Di
RTBuilder.hmilik LLVM Flang ada komentar TODO bahwa penggunaansizeofmengasumsikanbuild == host == target - Pada host Unix modern 64-bit,
sizeof(long)adalah 8 byte, tetapi pada targetwasm32-unknown-emscriptenseharusnya 4 byte - Saat argumen bertipe
CHARACTERFortran diteruskan ke fungsi atau subroutine, argumen tersembunyi yang membawa panjang string dapat ditambahkan - Di pustaka runtime Fortran, argumen panjang ini dideklarasikan sebagai
size_t, dan melalui rantaitypedefmenjadi sama denganunsigned long - Perbedaan ukuran argumen tersembunyi inilah yang menyebabkan ketidakcocokan
i64dani32
Patch sementara: memaksa nilai 4 byte
- Solusi idealnya adalah
flang-new, saat cross-compiling, mengeluarkani32ataui64sesuai arsitektur target dan model data, tanpa bergantung pada host - Untuk saat ini, digunakan patch yang meng-hardcode ukuran
longmenjadi 4 byte sesuaiwasm32dan Emscripten - Isi patch terbagi dua
- Di
RTBuilder.h, tipe model untuklongdanunsigned longdipaksa menjadi8 * 4, bukan8 * sizeof(...) - Di
CodeGen.cpp, argumen pemanggilanmalloc()dibuat sebagai integer 32-bit alih-alih 64-bit, dan ukuran alokasi di-cast kei32
- Di
- Perubahan ini juga memperbaiki alokasi dinamis berbasis
ALLOCATE()yang diperkenalkan di Fortran 90 - Setelah build ulang, jika
hello.f08danhello.cdi-link dengan pustaka runtime, build berhasil tanpa peringatan, dan Node menghasilkan output berikutHello, World!
Membangun BLAS ke WebAssembly
- BLAS adalah kumpulan rutin level rendah yang menjalankan operasi umum aljabar linear seperti perkalian matriks·vektor
- Rutin BLAS asli dirilis pada 1979 dan menjadi standar de facto di bidang komputasi numerik
- Implementasi referensi BLAS 3.12.0 ditulis dalam Fortran 90 dan tersedia dari netlib
- Build dilakukan dengan menentukan alat berikut di
make.incFC = ../build/bin/flang-newFFLAGS = -O2AR = emarRANLIB = emranlib
- Hasil build-nya adalah pustaka statis
blas_LINUX.a - Contoh rutin Fortran
barmemanggil rutin BLAS level 2ZGEMV() ZGEMV()menjalankan operasi matriks-vektor bilangan kompleks, dan contoh ini memakai argumenCOMPLEX(KIND=8)serta argumen pengaturanCHARACTER'N'- Program C membuat array bilangan kompleks, meneruskannya ke rutin Fortran, lalu mencetak hasilnya
- Hasil eksekusi Node sebagai berikut
Y[0]: 23.000000 + 6.000000iY[1]: 18.000000 + 10.000000iY[2]: 6.000000 + 16.000000i
- Hasil ini mengonfirmasi bahwa BLAS yang dikompilasi dari sumber Fortran 90 berjalan di bawah WebAssembly
Contoh browser: pengklasifikasi angka tulisan tangan
- Demo ini mengklasifikasikan angka 0–9 yang digambar tangan menggunakan jaringan saraf tiruan multilayer perceptron (MLP)
- Pengguna dapat menggambar angka dengan mouse atau layar sentuh, dan probabilitas relatif yang diprediksi jaringan ditampilkan pada plot di sebelah kanan
- Bobot model telah dilatih sebelumnya dengan Python, tetapi klasifikasi dilakukan di browser pada saat runtime menggunakan JavaScript dan WebAssembly
- Proses klasifikasi MLP pada dasarnya adalah pengulangan penjumlahan dan perkalian matriks-vektor
- Komputasi berat ditangani oleh satu subroutine Fortran yang menggunakan rutin BLAS level 2
DGEMV()
Membangun LAPACK ke WebAssembly
- LAPACK adalah pustaka perangkat lunak untuk menyelesaikan masalah aljabar linear secara numerik, dan dibangun di atas BLAS
- Implementasi referensi LAPACK 3.12.0 disediakan oleh netlib dan didistribusikan dengan lisensi BSD yang dimodifikasi
- Setelah menyalin
make.inc.examplemenjadimake.inc, pengaturan berikut diubahFCditetapkan ke path lengkapflang-newyang telah dibangunFFLAGS = -O2AR = emarRANLIB = emranlibTIMER = INT_CPU_TIME
- Perintah
make libmenghasilkan pustaka statis WebAssemblyliblapack.a - Setelah itu, rutin LAPACK dapat dipanggil dengan cara yang mirip contoh BLAS
Contoh browser: interpolasi polinomial menggunakan aljabar linear
- Demo ini mencari polinomial interpolasi untuk sekumpulan titik, dan menunjukkan bahwa rutin LAPACK berjalan di browser
- Saat pengguna mengklik plot untuk menambahkan titik baru, dicari polinomial interpolasi yang melewati semua titik
- Metode yang digunakan adalah Vandermonde’s method
- Persamaan aljabar linear yang diperoleh dengan cara ini diselesaikan secara numerik menggunakan rutin LAPACK
DGELS() - Polinomial derajat
n-1yang secara tepat mencakupntitik data selalu dapat ditemukan - Ketika
nmembesar, polinomial dapat berosilasi besar di antara titik data yang berurutan, yang disebut fenomena Runge, dan ini dapat dihindari dengan interpolasi spline
Tugas tersisa dan alat yang disediakan
- Dengan LLVM Flang yang telah dipatch, kode Fortran modern dapat dikompilasi menjadi objek WebAssembly
- Keunggulan cara ini adalah algoritma numerik untuk web tidak perlu ditulis ulang dalam JavaScript, melainkan dapat memakai alat dan pustaka Fortran yang sudah ada
- Jika
flang-newmendukung WebAssembly secara resmi, beban mempertahankan fork LLVM untuk webR dan paket R akan berkurang - Saat ini masih diperlukan jalur yang lebih baik untuk memperbaiki masalah LLVM Flang dan cross-compiling dengan benar di semua target
- Bagi pengguna yang sulit atau tidak ingin membangun LLVM Flang sendiri, tersedia container Docker berisi binary LLVM Flang yang telah dipatch di GitHub Container Registry
1 komentar
Komentar Hacker News
Sebagai sedikit latar belakang, eksplorasi Fortran ini adalah bagian dari pekerjaan WebR luar biasa yang sedang dikerjakan George untuk menjalankan R di browser
Di source R ada cukup banyak kode Fortran, dan setahu saya WebR awalnya mengompilasi Fortran ke C terlebih dahulu dengan f2c, lalu mengompilasi C itu ke wasm
Berkat patch LLVM Flang, WebR kini bisa dibangun dengan compiler Fortran sungguhan
George tidak mengatakannya langsung di posting blognya, tetapi ia pernah mengatakan berharap Flang menerima patch ini atau mengimplementasikannya dengan cara yang lebih baik
Kalau itu terjadi, tidak perlu lagi memelihara patch terpisah, dan Flang tanpa modifikasi bisa mengompilasi ke wasm, sehingga proyek lain yang memakai Fortran juga diuntungkan
https://docs.r-wasm.org/webr/latest/
Namun saya sedang fokus pada pekerjaan yang diperlukan untuk menyelesaikan pengembangan produk Fortran Nvidia, jadi tidak punya waktu tersisa untuk pekerjaan seperti ini
20 tahun lalu saya pernah mengerjakan kompilasi FORTRAN di Xilinx, dan yang saya ingat hanya ada definisi
barfdi file header f2c.h/* 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)
Saya sangat suka penggunaan contoh nontrivial paling sederhana sebagai cara menjelaskan
Karena tulisannya didasarkan pada masalah konkret “memanggil fungsi BLAS dari JavaScript”, saya merasa banyak belajar; ini tulisan yang bagus
[1] https://en.m.wikipedia.org/wiki/The_Theoretical_Minimum
Saya tidak tahu harus kagum atau ngeri, dan mungkin keduanya
Saat membangun f18, saya sarankan memakai source terbaru llvm-project/main
Proyek ini bergerak cepat, jadi akan membuang waktu jika men-debug masalah yang sudah diperbaiki atau melewatkan fitur yang sudah diimplementasikan
Apakah tujuannya mengerjakan port WebAssembly agar intermediate code bisa sampai ke titik yang berjalan untuk Fortran?
Saya tidak terlalu paham pengembangan WebAssembly
Dari sudut pandang konsumen, apakah WebAssembly saat ini sudah menawarkan sesuatu? Atau lebih merupakan pekerjaan fondasi untuk masa depan ketika program benar-benar menjadi portabel?
Saya pernah dengar perangkat WebAssembly mempermudah pembatasan akses seperti jaringan atau file, tetapi saya tidak tahu apakah itu masih teori atau sudah diimplementasikan
Perbedaan utamanya adalah Wasm sendiri tidak memiliki standard library dan tidak mengekspos fungsi input/output
Jadi host, yaitu VM-nya, bisa dibuat sendiri dan mengekspos fungsi yang akan dipakai binary Wasm, dan binary Wasm hanya bisa mengakses dunia luar melalui fungsi-fungsi tersebut
Keunggulan lainnya adalah format binernya tidak proprietari dan memiliki spesifikasi, sehingga siapa pun bisa mengimplementasikan Wasm VM
Namun saat ini belum bisa dibilang sudah dalam kondisi baik; masih terlalu dini, banyak fitur baru sedang distandardisasi oleh W3C dan kelompok serupa, dan prosesnya juga sangat lambat
Ada juga cara untuk mendistribusikan atau melakukan cross-compile ke sebagian besar target
Mirip seperti biasanya orang tidak tahu dan tidak terlalu peduli apakah CPU komputernya ARM atau x86
Jadi jika Anda tidak peduli pada detail seperti apakah itu berjalan sebagai kode native atau di atas VM seperti JVM, .NET, atau WASM, sulit mengatakan apa yang ditawarkannya dibanding solusi lain
Biasanya hanya contoh buruk yang terlihat, lalu itu berubah menjadi meme seperti “semua program Electron adalah monster buncit pemakan sumber daya, dan semua aplikasi native otomatis merupakan keajaiban rekayasa perangkat lunak yang efisien”
Sayang sekali saya tidak menyimpan kode Fortran 78 yang saya tulis pada 1981/82; kalau ada, saya bisa menguji apakah bisa berjalan di sini
Itu adalah formatter source code untuk bahasa pemrograman Jovial, dan sebenarnya bukan pekerjaan yang cocok dilakukan dengan Fortran, tetapi saat itu hanya itulah pilihannya
Apakah ada ekosistem aljabar linear yang cukup siap produksi untuk dipakai dari JavaScript?
Saat mencari, yang biasanya muncul hanya port JavaScript dari library lama yang sudah akrab sekitar 10 tahun, misalnya port lewat emscripten, jadi saya penasaran apakah ada yang saya lewatkan
Aneh rasanya jika LFortran tidak dibahas lebih dalam
Ada juga contoh WASM yang hebat dan mengagumkan yang berjalan secara online
https://dev.lfortran.org/
Pada 2020, banyak fiturnya masih belum ada dan hanya mendukung subset Fortran yang sangat kecil, tetapi sekarang sudah mendukung fitur bahasa yang jauh lebih luas dan dapat mengompilasi cukup banyak kode Fortran
Kompilasi native ke WebAssembly juga dimungkinkan
Namun, para pengembang mengatakan masih ada bagian-bagian yang kasar untuk menggunakan LFortran, proyek ini saat ini dianggap berada pada tahap alfa, dan masalah bisa muncul saat mengompilasi kode nyata
Beberapa proyek seperti MINPACK dapat dikompilasi dengan sukses, tetapi karena belum mendukung seluruh spesifikasi Fortran, cukup banyak proyek yang lebih besar masih belum bisa dikompilasi
Para pengembang LFortran menargetkan dukungan penuh Fortran 2018, dan salah satu fitur yang menonjol adalah REPL Fortran interaktif seperti Jupyter
Setelah beberapa tahun pengembangan lagi, ini tampaknya akan menjadi pilihan yang sangat baik untuk mengompilasi kode Fortran ke WebAssembly
Artikel itu juga menyarankan untuk melihat demo LFortran di https://dev.lfortran.org; sangat mengesankan, tetapi perubahan pertama yang saya coba, yaitu mengubah
x * 2menjadix * 3, saat ini belum didukung oleh code generator-nyaAda juga Fortran di atas .NET dan Java
https://www.silverfrost.com/14/ftn95/ftn95_fortran_95_for_microsoft_dotnet_features.aspx
https://dl.acm.org/doi/10.1145/376656.376833
Saat mengerjakan https://medium.com/@tomasreimers/compiling-tensorflow-for-the-browser-f3387b8e1e1c, saya merasa sangat beruntung TensorFlow menggunakan Eigen, bukan BLAS/Lapack, library matematika populer yang ditulis dalam Fortran
Kalau tidak, pekerjaannya akan jauh lebih banyak