Bend - Bahasa Tingkat Tinggi yang Berjalan di GPU (Menggunakan HVM2)
(github.com/HigherOrderCO)- Bend adalah bahasa pemrograman paralel tingkat tinggi yang menargetkan ekspresivitas seperti Python·Haskell sekaligus eksekusi paralel masif ala CUDA, dan berjalan di atas runtime HVM2
- Mendukung higher-order function dengan closure, alokasi objek cepat, rekursi tanpa batas, dan continuation, sambil berjalan di hardware paralel seperti GPU tanpa notasi paralelisasi eksplisit seperti pembuatan thread, lock, mutex, atau atomic
- Sasaran desain saat ini adalah performa yang menskalakan sesuai jumlah core, dan mendukung lebih dari 10.000 thread serentak, tetapi versi saat ini mungkin memiliki performa single-core yang rendah, sementara peningkatan code generation dan optimisasi masih berlangsung
- Cara eksekusi terbagi menjadi
bend run-rs,bend run-c, danbend run-cu; kode yang dapat diparalelkan bisa dijalankan secara paralel di interpreter C atau interpreter CUDA hanya dengan mengganti perintah eksekusi - Dukungan Windows masih dalam pengerjaan sehingga WSL2 menjadi alternatif, dan eksekusi GPU saat ini hanya mendukung GPU NVIDIA
Model pemrograman yang dituju Bend
- Bend adalah bahasa pemrograman yang berjalan di hardware paralel berskala besar sambil tetap mempertahankan pengalaman penggunaan bahasa tingkat tinggi
- Menyediakan fitur bahasa ekspresif seperti Python dan Haskell
- Alokasi objek cepat
- Higher-order function dengan closure
- Rekursi tanpa batas
- Continuation
- Berjalan di hardware paralel berskala besar seperti GPU layaknya CUDA, dengan target akselerasi yang hampir linear berdasarkan jumlah core
- Untuk eksekusi paralel, tidak perlu menulis hal-hal berikut secara langsung
- Pembuatan thread
- Lock
- Mutex
- Atomic
- Runtime menggunakan HVM2
Batasan dan hal yang perlu diperhatikan saat ini
- Bend berfokus pada penskalaan performa sesuai jumlah core, dan dirancang untuk mendukung lebih dari 10.000 thread serentak
- Versi saat ini mungkin memiliki performa single-core yang rendah
- Performa diperkirakan akan meningkat seiring berkembangnya code generation dan teknik optimisasi
- Dukungan Windows masih dalam pengerjaan, dan sebagai alternatif dapat menggunakan WSL2
- Dukungan GPU saat ini hanya mendukung GPU NVIDIA
Instalasi dan cara menjalankan
- Linux dan Mac sama-sama memerlukan Rust terpasang
- Bend versi C menggunakan GCC, dan README merekomendasikan GCC 12.x atau lebih rendah
- Untuk menggunakan runtime CUDA, perlu memasang CUDA Toolkit 12.x untuk Linux
- HVM2 dipasang dengan
cargo install hvm, dan Bend dipasang dengancargo install bend-lang - Perintah untuk menjalankan program Bend dibagi berdasarkan runner
bend run <file.bend>: menggunakan interpreter C secara default, eksekusi paralelbend run-rs <file.bend>: menggunakan interpreter Rust, eksekusi sekuensialbend run-c <file.bend>: menggunakan interpreter C, eksekusi paralelbend run-cu <file.bend>: menggunakan interpreter CUDA, eksekusi paralel masif
- Dengan
gen-cdangen-cu, program dapat dikompilasi menjadi file C/CUDA mandiri yang dapat dieksekusi - Code generator masih tahap awal dan belum sematang compiler seperti GCC atau GHC
- Dengan flag
-s, jumlah reduction, waktu eksekusi, dan jumlah interaction per detik dapat dilihat
Contoh penjumlahan sekuensial dan penjumlahan paralel
- Contoh penjumlahan di README membandingkan dua cara kode yang menjumlahkan angka dari
starthinggatarget - Versi sekuensial memiliki struktur yang menambahkan
startsaat ini ke hasilSum(start + 1, target)- Perhitungan berikutnya bergantung pada hasil penjumlahan sebelumnya
- Sebelum perhitungan saat ini selesai, proses tidak bisa lanjut ke tahap berikutnya sehingga tidak dapat diparalelkan
- Contohnya memanggil
Sum(1, 1_000_000), dan mencakup komentar bahwa nilai tersebut bisa melampaui nilai maksimum angka Bend
- Versi yang dapat diparalelkan membagi rentang menjadi dua, lalu menghitung jumlah sisi kiri dan kanan secara rekursif
- Perhitungan
(3 + 4)tidak bergantung pada perhitungan(1 + 2) - Kedua perhitungan dapat terjadi bersamaan sehingga eksekusi paralel dimungkinkan
- Perhitungan
- Di Bend, jika kode dapat berjalan secara paralel, eksekusi paralel bisa dilakukan hanya dengan mengganti perintah eksekusi
Contoh performa Bitonic Sorter
- README menyajikan bitonic sorter yang diimplementasikan dengan rotasi pohon immutable sebagai contoh kecepatan
- Algoritme ini termasuk tipe yang tidak mudah diharapkan cepat di GPU, tetapi Bend menjalankannya di banyak thread dengan pendekatan divide-and-conquer
- Tidak perlu pembuatan thread atau pengelolaan lock secara eksplisit
- Hasil benchmark adalah sebagai berikut
bend run-rs: CPU, Apple M3 Max, 12,15 detikbend run-c: CPU, Apple M3 Max, 0,96 detikbend run-cu: GPU, NVIDIA RTX 4090, 0,21 detik
- Algoritme lain dapat dilihat di folder examples
Referensi
- Teknologi dasar Bend dapat dilihat di paper HVM2
- Dokumentasi resmi masih dalam pengerjaan, dan penjelasan lebih mendalam ada di GUIDE.md
- Daftar fitur dapat dilihat di FEATURES.md
- Bend dikembangkan oleh HigherOrderCO
1 komentar
Pendapat Hacker News
Saya mencoba memindahkan contoh
sumke Python murni; denganpypy3butuh 4,478 detik di satu thread, sedangkan Python 3.12 butuh 1 menit 42,148 detikSebaliknya, versi Bend satu thread masih berjalan di laptop saya setelah 42 menit dan belum selesai meski memakai memori 6 GB. Lingkungannya 12th Gen Intel(R) Core(TM) i7-1270P, Ubuntu 24.04
Kalau selambat ini pada contoh yang sangat sederhana, sulit berharap banyak untuk tugas yang kompleks, dan saya penasaran apakah sudah diuji atau dikembangkan di lingkungan selain Mac/aarch64. Nanti saya berencana menjalankannya lagi dengan argumen
-sPada contoh
sum, Bend punya kerugian besar karena mengalokasikan 2 node IC untuk setiap operasi angka, sementara Python tidak. Seperti HVM1, ini akan segera bisa dihindari, tetapi belum diimplementasikan di HVM2Sebagian besar pekerjaan Bend dihabiskan untuk membuat evaluator paralel bekerja dengan benar, dan menjalankan closure serta rekursi tanpa batas di GPU itu sangat sulit. Karena bagian itu baru saja selesai, hampir belum ada upaya untuk optimasi mikro, dan pembuatan kode HVM2 juga masih buruk
Jika dibandingkan dengan kasus seperti contoh
Bitonic Sort, di mana kedua sisi melakukan jumlah alokasi yang sama, performa sebenarnya bisa dilihat dengan lebih adil. HVM1 hanya sekitar 3x lebih lambat dari GHC pada satu core, dan saya rasa HVM2 juga bisa segera mencapai level ituSaya paham bahwa ucapan “masih buruk, tapi akan membaik” bisa terasa mengecewakan. Namun, karena fondasinya sudah ada, optimasi mikro adalah bagian yang paling mudah, dan saya yakin performanya akan naik besar dari sini
Bahasa ini menargetkan aplikasi GPU yang intensif komputasi dan masih berada pada tahap awal. Rekursi bukan aplikasi targetnya, dan menurut saya sulit dianggap sebagai benchmark yang relevan
Mirip dengan bagaimana ISPC dapat mengompilasi agar 32 pemanggilan fungsi dijalankan bersamaan untuk setiap thread CPU. Misalnya, jika memakai data 16-bit di AVX512, 32 core × 2 thread SMT per core × 32 eksekusi compiler dapat menghasilkan 2048 eksekusi berjalan bersamaan
Implementasi yang Pythonic kemungkinan akan memakai loop dan state yang dapat diubah
+0diperlukan. Bukankah itu operasi yang tidak melakukan apa-apa?Ada banyak reaksi negatif di thread ini, tetapi saya ingin memberi kudos kepada penulisnya hanya karena sudah membangun sampai sejauh ini
Untuk proyek serupa, saya hanya tahu Futhark, tetapi sintaksnya bergaya Haskell, sehingga bisa cukup sulit dipahami bagi developer umum yang terbiasa dengan C/C++/Python/JS/Java dan sebagainya
Kekurangan terbesarnya adalah, tidak seperti Futhark, targetnya hanya CUDA atau multicore. Futhark dapat menargetkan OpenCL, CUDA, ISPC, HIP, CPU single-core, dan CPU multicore. Menurut saya masalah performa yang ditunjukkan orang lain sangat mungkin diselesaikan
Contoh singkat: https://github.com/m4rs-mt/ILGPU/blob/master/Samples/SimpleM...
Juga mendukung fitur tingkat lanjut seperti assembly PTX inline: https://github.com/m4rs-mt/ILGPU/blob/master/Samples/InlineP...
NVIDIA juga pernah mensponsori varian Haskell, .NET, Java, dan Julia untuk CUDA, ada juga Python JIT, dan mereka juga bekerja sama dengan pihak Mojo
Dibuat oleh Tucker Taft, yang telah menjadi perancang Ada sejak 1995, dan sebagian fitur paralel ParaSail masuk ke Ada 2022
OP sering membawa hal-hal paling keren yang baru-baru ini muncul di HN, jadi sayang rasanya ia hanya mendapat kritik panjang meski jelas ini masih versi awal
Sebaliknya, cara untuk tepat dalam kritik itu terbatas, sedangkan cara untuk keliru banyak, sehingga kritik bisa bervariasi tanpa henti. Akibatnya komentar positif hanya beberapa, sementara sebagian besar terlihat seperti kritik atau “seharusnya juga melakukan ini”. Ini bukan kesalahan individu tertentu, melainkan lebih merupakan budaya teknolog masa kini
Jika orang hanya menyembunyikan kebenaran pahit di balik tepuk tangan, dunia akan runtuh
Kritik juga berarti orang tertarik dan terlibat dengan ide serta pendekatannya, jadi sering kali itu merupakan sinyal positif
Namun, mengkritik proyek yang membuat klaim yang menyesatkan, kurang berdasar, atau palsu juga merupakan norma sosial yang baik. Karena itu membuat klaim semacam itu berkurang
Sesuatu yang sulit dipahami sering terasa mengancam, dan kritik adalah reaksi umum terhadap ancaman sekaligus bentuk respons yang dapat dilakukan dengan pemahaman paling sedikit
Situs webnya benar-benar dibuat dengan sangat baik. Langsung jelas terlihat apa yang dilakukannya
Orang-orang yang menangani “combinator” biasanya ingin memakai banyak istilah teknis yang menakutkan, tetapi OP benar-benar menunjukkan ide sederhana di balik alat ini. Saya suka karena ini kebalikan dari pendekatan akademis yang memperlihatkan sampai detail terakhir tetapi tidak mengatakan apa yang sebenarnya terjadi. Semestinya lebih banyak yang memakai cara seperti ini
Secara teori keren dan saya memahami proposisi nilainya, tetapi jujur saja saya tidak merasa ini akan menjadi alat yang relevan di dunia nyata
Ini catatan setelah kesan pertama dan membaca sekilas makalahnya. Saya tahu ini perangkat lunak yang masih sangat awal
Bend terlihat seperti DSL yang sangat terbatas. Tidak ada FFI, tidak ada cara berinteraksi dengan buffer mentah, dan format floating-point 24-bit juga aneh
Ada alasan mengapa IC tidak menjadi arus utama. Performa kemungkinan besar akan tetap buruk, dan penelusuran graf tidak cocok dengan hardware
Premis optimal reduction memang valid, tetapi pada akhirnya kernel harus ditulis dengan cara yang bisa diparalelkan. Artinya tidak boleh ada dependensi data, dan penggunaan rekursi juga perlu dipertimbangkan
Tidak ada contoh serius yang membandingkan langsung kode Bend/HVM dengan program OMP/CUDA yang setara. Sulit menilai seberapa besar kompleksitas implementasi berkurang dan seperti apa performanya
Dalam komputasi paralel berkinerja tinggi di dunia nyata, struktur berbentuk pohon hampir tidak ada dan array adalah raja. Ini karena sifat fisik memori yang bekerja di level hardware. Yang paling cocok untuk buffer memori kontigu yang dapat diubah adalah loop. Kalau HVM mengimplementasikan ini, saya akan mengamatinya
Saat ini tampak hampir sepenuhnya terisolasi dari data eksternal, sangat lambat, dan seperti bahasa setengah matang yang menaruh abstraksi raksasa di atas hardware. Ia juga tidak bisa memanfaatkan fitur seperti cache bertingkat, tensor core, SIMD, dan operasi atomik
Maaf kalau terdengar kasar, tetapi saya tetap menganggap implementasi teknis dan latar teorinya sangat menarik. Hanya saja saya belum yakin dengan kegunaannya di dunia nyata
FFI sudah diimplementasikan, tetapi belum kami rilis. Alasannya karena kami ingin merilisnya bersama rendering grafis, dan saya rasa hasilnya akan cukup keren
Haskell/GHC juga memakai graf dan pohon, tetapi tidak ada yang akan mengatakan itu tidak praktis. Memang benar array adalah raja, tetapi cukup banyak algoritma modern yang tidak cocok dengan array—seperti compiler, type checker, solver, dan sebagainya—diimplementasikan dengan Haskell
Alasan utama IC tidak cepat adalah karena belum ada yang benar-benar melakukan optimasi level rendah di atasnya. Semua implementasi lama sangat tidak efisien, dan pekerjaan saya sejauh ini pun menghabiskan waktu untuk membuatnya berjalan dengan benar di GPU
Seperti halnya fakta bahwa loop pun belum ada, solusinya ya tinggal menambahkan loop. Jika Anda mengira ada batasan mendasar di sana, Anda mungkin akan terkejut
HVM2 akhirnya menjadi algoritma yang benar dan dapat diskalakan, dan sekarang saatnya mengoptimalkan performa level rendah yang sebenarnya
Dalam algoritma Fast Multipole atau Barnes-Hut, urutan Morton atau urutan H-index digunakan untuk menurunkan operasi berpasangan O(n²) masing-masing menjadi O(n), O(n log n). Barnes-Hut lebih umum di astrofisika, sedangkan Fast Multipole lebih sering terlihat di dinamika molekuler kimia
Saya mengikuti 15-210, mata kuliah algoritma paralel di CMU, 10 tahun lalu. Mereka menjelaskan bahwa ketika Hukum Moore mencapai batasnya, paralelisme akan menjadi masa depan komputasi, dan saya cukup diyakinkan oleh argumen itu sehingga ingin bereksperimen
Namun tidak banyak pilihan pemrograman paralel serbaguna. Bahkan SML yang dipakai di kelas pun tidak paralel, dan di bagian akhir ada seksi yang memakai ekstensi dan CUDA, tetapi seingat saya itu terbatas
Setelah itu, Rust membuat saya bisa sedikit bereksperimen dengan multithreading, dan Shadertoy memungkinkan saya melakukan pekerjaan kreatif dengan shader. Tetapi bahasa paralel serbaguna di atas GPU—saya sangat antusias untuk mencobanya langsung
Jika Anda menyukai 210, Anda mungkin juga akan menyukai https://futhark-lang.org/. Itu bahasa keluarga ML, dikompilasi ke GPU, dan performanya juga bagus
Idenya sangat keren, tetapi kalau bukan saya yang melewatkan sesuatu, ini terlihat sangat lambat
Saya menulis loop sederhana di C++ yang menjumlahkan dari 0 sampai 2³⁰, dan tanpa optimasi, single-thread di laptop saya butuh 1,7 detik, kira-kira mirip dengan performa Bend di RTX 4090. Dengan
-O3, loop itu divektorisasi dan berjalan di bawah 80 msJika dibandingkan dengan program C yang benar-benar melakukan alokasi, Bend mungkin lebih cepat bahkan hanya dengan beberapa thread
Code generation Bend memang masih buruk, tetapi ini termasuk low-hanging fruit. Sebagian besar pekerjaan sejauh ini dihabiskan untuk membuat evaluator paralel yang sangat sulit itu benar
Saya tahu ini terdengar seperti “percaya saja”, tetapi begitu kami mulai melakukan kompilasi prosedural, pembuatan loop, dan sebagainya, performa single-thread akan jauh membaik. Hanya saja belum kami lakukan
Saya juga sempat berpikir mungkin seharusnya menunggu sedikit lebih lama sebelum benar-benar mempublikasikannya
objdumpapakah loop itu benar-benar divektorisasi, atau justru compiler mengoptimalkannya habis-habisanLoop tersebut menyebabkan signed integer overflow, dan di C++ itu adalah undefined behavior. Compiler secara sah boleh menghasilkan hasil apa pun
Untuk menghindarinya,
sumharus dideklarasikan sebagaiunsigned. Overflow integer unsigned terdefinisi dengan baik, dan optimasi tetap terjadi, tetapi setidaknya kebenarannya terjamin-O3di clang, loop-nya dihapus sepenuhnya: https://godbolt.org/z/M1rMY6qM9Mungkin itu bukan perbandingan yang adil
Tentu saja, saya juga mungkin melewatkan maksudnya
Saya ingin mengucapkan selamat kepada penulisnya. Ini benar-benar pekerjaan yang keren
Membuat automatic parallelization yang benar sama sekali bukan hal mudah, dan Anda pantas merasa bangga. Saya menantikan bagaimana proyek ini akan berkembang
Saya tidak paham kenapa reaksinya begitu banyak yang negatif. Rasanya seperti massa yang marah, seperti bot yang mengorek celah di README sambil mencoba mengubah konteks dan maksud tulisannya
Berdebat berjam-jam tanpa meluangkan bahkan 2 menit untuk membaca dengan benar itu bodoh dan kejam. OP sudah sampai sejauh ini sebagai proyek satu orang, jadi saya harap ia terus maju
Saya penasaran apakah HVM2 mengompilasi interaction net, misalnya ke SPIR-V, atau seperti HVM asli, ia adalah interpreter yang berjalan di GPU
Dulu saya pernah mencoba mengompilasi interaction net ke C dengan cara mereduksi program sebanyak mungkin lalu tidak mereduksi input, memperlakukannya seperti optimisasi seluruh program. Menargetkan bahasa shader sepertinya juga tidak akan terlalu sulit
Dari repositorinya, tertulis bahwa mereka menyediakan bahasa IR level rendah untuk mendeskripsikan net HVM2 dan compiler ke C/CUDA: https://github.com/HigherOrderCO/HVM
Namun setelah saya lihat lagi, runtime CUDA HVM2 tampak seperti interpreter yang menelusuri graf di memori dan menerapkan reduksi: https://github.com/HigherOrderCO/HVM/blob/5de3e7ed8f1fcee6f2...
Yang saya maksud adalah pendekatan yang menelusuri interaction net untuk memulihkan term yang mendekati ekspresi kalkulus lambda, lalu menurunkannya menjadi potongan-potongan kecil C guna meminimalkan overhead runtime
Motivasi jujurnya adalah bahwa dengan Bend, sulit untuk mengalahkan kernel GPU yang ditulis tangan pada beban kerja seperti ML. Secara teori HVM bisa berperan sebagai perekat yang menyambungkan kernel-kernel komputasi dan memparalelkan urutan eksekusinya, tetapi untuk itu dibutuhkan FFI yang baik
Interaction net sulit diterjemahkan melintasi batas FFI, tetapi jika node kernel komputasi FFI ditempatkan di dalam jaringan interaksi dan net-nya dikompilasi ke C, FFI yang masuk akal dapat dipulihkan tanpa overhead penerjemahan
Pilihan lain adalah mengimplementasikan HVM di hardware, dan saya sedang sedikit mengutak-atiknya di FPGA yang menganggur
Belum menargetkan SPIR-V secara langsung, tetapi itu menjadi tujuan
Compiler C menghasilkan peningkatan kecepatan seperti yang diharapkan, yaitu 3–4x dan segera lebih dari itu, tetapi runtime CUDA tidak memperoleh peningkatan kecepatan besar dibandingkan versi yang tidak dikompilasi
Saya menduga penyebabnya adalah divergensi warp. Pada prosedur yang tidak dikompilasi, semua pemanggilan fungsi dapat digabungkan ke dalam satu ekspander fungsi bergaya interpreter yang “generik”, dan thread-thread warp dapat melakukan reduksi tanpa percabangan. Ke depan, bagian ini akan diteliti lebih mendalam