WebAssembly (wasm) adalah format instruksi biner portabel. Kode wasm kode yang sama dapat dieksekusi di lingkungan apa pun. Untuk mendukung pernyataan ini, setiap bahasa, platform, dan sistem harus dapat mengeksekusi kode tersebut, membuatnya secepat dan seaman mungkin.
Wasmer adalah runtime wasm yang ditulis dalam
Rust . Jelas, wasmer dapat digunakan dalam aplikasi Rust apa pun. Penulis materi, terjemahan yang kami terbitkan hari ini, mengatakan bahwa ia dan peserta lain dalam proyek Wasmer berhasil mengimplementasikan runtime kode-was ini dalam bahasa lain:
Di sini kita akan berbicara tentang proyek baru -
go-ext-wasm , yang merupakan perpustakaan untuk Go, yang dirancang untuk mengeksekusi kode biner-biner. Ternyata, proyek pengembangan-jauh jauh lebih cepat daripada solusi serupa lainnya. Tapi jangan maju dulu. Mari kita mulai dengan sebuah cerita tentang bagaimana bekerja dengannya.
Memanggil fungsi wasme dari Go
Untuk memulai, instal wasmer di lingkungan Go (dengan dukungan cgo).
export CGO_ENABLED=1; export CC=gcc; go install github.com/wasmerio/go-ext-wasm/wasmer
Proyek
go-ext-wasm adalah perpustakaan Go biasa. Saat bekerja dengan perpustakaan ini, konstruk
import "github.com/wasmerio/go-ext-wasm/wasmer"
.
Sekarang mari kita berlatih. Kami akan menulis program sederhana yang dikompilasi dalam wasm. Kami akan menggunakan ini, misalnya, Karat:
#[no_mangle] pub extern fn sum(x: i32, y: i32) -> i32 { x + y }
Kami memanggil file dengan program
simple.rs
, sebagai hasil dari kompilasi program ini kami mendapatkan file
simple.wasm .
Program berikut, yang ditulis dalam Go, menjalankan fungsi
sum
dari file wasm, dengan memberikan angka 5 dan 37 sebagai argumen:
package main import ( "fmt" wasm "github.com/wasmerio/go-ext-wasm/wasmer" ) func main() {
Di sini, sebuah program yang ditulis dalam Go memanggil fungsi dari file wasm yang diperoleh dengan mengkompilasi kode yang ditulis dalam Rust.
Jadi, percobaan itu berhasil, kami berhasil mengeksekusi kode WebAssembly di Go. Perlu dicatat bahwa konversi tipe data bersifat otomatis. Nilai-nilai Go yang dilewatkan ke kode wasm dilemparkan ke tipe WebAssembly. Apa yang dikembalikan fungsi wasm ke tipe Go. Akibatnya, bekerja dengan fungsi dari file wasm di Go terlihat sama dengan bekerja dengan fungsi Go biasa.
Fungsi Panggilan Pergi dari Kode WebAssembly
Seperti yang kita lihat pada contoh sebelumnya, modul WebAssembly dapat mengekspor fungsi yang dapat dipanggil dari luar. Ini adalah mekanisme yang memungkinkan kode wasme dijalankan di berbagai lingkungan.
Pada saat yang sama, modul WebAssembly sendiri dapat bekerja dengan fungsi yang diimpor. Pertimbangkan program berikut yang ditulis dalam Rust.
extern { fn sum(x: i32, y: i32) -> i32; } #[no_mangle] pub extern fn add1(x: i32, y: i32) -> i32 { unsafe { sum(x, y) } + 1 }
Beri nama file dengan
import.rs
. Mengompilasinya ke dalam WebAssembly akan menghasilkan kode yang dapat ditemukan di
sini .
Fungsi
add1
diekspor
add1
fungsi
sum
. Tidak ada implementasi dari fungsi ini, hanya tanda tangannya yang ditentukan dalam file. Inilah yang disebut fungsi eksternal. Untuk WebAssembly, ini adalah fungsi yang diimpor. Implementasinya harus diimpor.
Kami mengimplementasikan fungsi
sum
menggunakan Go. Untuk ini, kita perlu menggunakan
cgo . Ini kode yang dihasilkan. Beberapa komentar, yang merupakan deskripsi dari fragmen kode utama, diberi nomor. Di bawah ini kita akan membicarakannya secara lebih rinci.
package main
Mari kita uraikan kode ini:
- Tanda tangan dari fungsi
sum
didefinisikan dalam C (lihat komentar pada perintah import "C"
). - Implementasi fungsi
sum
didefinisikan dalam Go (perhatikan baris //export
- mekanisme ini dapat digunakan untuk membuat koneksi kode yang ditulis dalam Go dengan kode yang ditulis dalam C). NewImports
adalah API yang digunakan untuk membuat impor WebAssembly. Dalam kode ini, "sum"
adalah nama fungsi yang diimpor oleh WebAssembly, sum
adalah penunjuk ke fungsi Go, dan C.sum
adalah penunjuk ke fungsi cgo.- Dan akhirnya,
NewInstanceWithImports
adalah konstruktor yang dirancang untuk menginisialisasi modul WebAssembly dengan impor.
Membaca data dari memori
Contoh WebAssembly memiliki memori linier. Mari kita bicara tentang cara membaca data darinya. Mari kita mulai, seperti biasa, dengan kode Rust, yang akan kita sebut
memory.rs
.
#[no_mangle] pub extern fn return_hello() -> *const u8 { b"Hello, World!\0".as_ptr() }
Hasil kompilasi kode ini ada di file
memory.wasm
, yang digunakan di bawah ini.
Fungsi
return_hello
mengembalikan pointer ke string. Baris berakhir, seperti dalam C, dengan karakter nol.
Sekarang pergi ke sisi Go:
bytes, _ := wasm.ReadBytes("memory.wasm") instance, _ := wasm.NewInstance(bytes) defer instance.Close()
Fungsi
return_hello
mengembalikan pointer sebagai nilai
i32
. Kami mendapatkan nilai ini dengan menghubungi
ToI32
. Kemudian kita mendapatkan data dari memori menggunakan
instance.Memory.Data()
.
Fungsi ini mengembalikan potongan memori instance WebAssembly. Anda dapat menggunakannya seperti slice Go.
Untungnya, kita tahu panjang baris yang ingin kita baca, oleh karena itu, untuk membaca informasi yang diperlukan, cukup menggunakan
memory[pointer : pointer+13]
. Kemudian data yang dibaca dikonversi menjadi string.
Berikut adalah contoh yang menunjukkan mekanisme memori yang lebih maju saat menggunakan kode WebAssembly Go.
Tingkatan yang dicapai
Proyek go-ext-wasm, seperti yang baru saja kita lihat, memiliki API yang nyaman. Sekarang saatnya berbicara tentang kinerjanya.
Tidak seperti PHP atau Ruby, dunia Go sudah memiliki solusi untuk bekerja dengan kode wasm. Secara khusus, kita berbicara tentang proyek-proyek berikut:
- Life from Perlin Network - Juru bahasa WebAssembly.
- Go Interpreter's Wagon adalah interpreter dan toolkit WebAssembly.
Materi pada proyek php-ext-wasm menggunakan algoritma
n-body untuk mempelajari kinerja. Ada banyak algoritma lain yang cocok untuk memeriksa kinerja lingkungan eksekusi kode. Sebagai contoh, ini adalah algoritma
Fibonacci (versi rekursif) dan
algoritma Pollard ฯ yang digunakan dalam Life. Ini adalah algoritma kompresi Snappy. Yang terakhir ini bekerja dengan sukses dengan go-ext-wasm, tetapi tidak dengan Life atau Wagon. Akibatnya, ia dikeluarkan dari set tes. Kode uji dapat ditemukan di
sini .
Selama pengujian, versi terbaru dari proyek penelitian digunakan. Yaitu, ini adalah Life 20190521143330-57f3819c2df0 dan Wagon 0.4.0.
Angka-angka yang ditunjukkan pada grafik mencerminkan nilai rata-rata yang diperoleh setelah 10 awal tes. Studi ini menggunakan 2016 MacBook Pro 15 "dengan prosesor Intel Core i7 2,9 GHz dan memori 16 GB.
Hasil tes dikelompokkan sepanjang sumbu X sesuai dengan jenis tes. Sumbu Y menunjukkan waktu, dalam milidetik, yang diperlukan untuk menyelesaikan tes. Semakin kecil indikatornya, semakin baik.
Perbandingan kinerja Wasmer, Wagon dan Life menggunakan implementasi dari berbagai algoritmaPlatform Life and Wagon, secara rata-rata, memberikan hasil yang hampir sama. Wasmer, rata-rata, adalah 72 kali lebih cepat.
Penting untuk dicatat bahwa Wasmer mendukung tiga backend:
Singlepass ,
Cranelift dan
LLVM . Backend default di Go library adalah Cranelift (di
sini Anda dapat mengetahui lebih lanjut tentang itu). Menggunakan LLVM akan memberikan kinerja yang mendekati aslinya, tetapi diputuskan untuk memulai dengan Cranelift, karena backend ini memberikan rasio terbaik antara waktu kompilasi dan waktu eksekusi program.
Di sini Anda dapat membaca tentang berbagai backend, pro dan kontra mereka, dan dalam situasi apa lebih baik menggunakannya.
Ringkasan
Proyek open source
go-ext-wasm adalah Go library baru yang dirancang untuk mengeksekusi kode biner wasme. Ini termasuk
runtime Wasmer . Versi pertamanya termasuk API, kebutuhan yang paling sering muncul.
Tes kinerja menunjukkan bahwa Wasmer, rata-rata, 72 kali lebih cepat daripada Life and Wagon.
Pembaca yang budiman! Apakah Anda berencana menggunakan kemampuan untuk menjalankan kode wasme di Go menggunakan go-ext-wasm?
