WebAssembly dalam TypeScript
Artikel ini menjelaskan tentang WebAssembly dalam TypeScript.
Kami akan menjelaskan metode praktis dan mudah dipahami untuk mengintegrasikan TypeScript dengan WebAssembly.
YouTube Video
WebAssembly dalam TypeScript
WebAssembly (Wasm) adalah runtime berbentuk biner yang berjalan dengan kecepatan hampir setara native di dalam browser. Dengan memanggil Wasm dari TypeScript, Anda dapat memanfaatkan proses yang memerlukan komputasi tinggi dan pustaka native yang sudah ada, yang ditulis dalam C/C++ atau Rust, secara efisien.
Alur Eksekusi Dasar
Di sini, kami akan menjelaskan alur eksekusi dasar dari Wasm. TypeScript (atau browser) mengambil file .wasm, menginstansiasinya, dan memanggil fungsi yang diekspor.
-
- Buat biner .wasm menggunakan AssemblyScript, Rust, C++, atau siapkan yang sudah ada.
-
- Ambil file .wasm di TypeScript (atau browser) dan instansiasikan secara sinkron atau asinkron.
-
- Panggil fungsi yang diekspor dan bagikan memori dengan menggunakan
WebAssembly.Memoryjika diperlukan.
- Panggil fungsi yang diekspor dan bagikan memori dengan menggunakan
WebAssembly.instantiateStreaming
Selanjutnya, kami akan menunjukkan contoh dasar memuat file Wasm dan memanggil fungsi yang diekspor. Browser harus mendukung instantiateStreaming.
Kode berikut adalah contoh mengambil simple.wasm dari server dan memanggil fungsi add.
1// TypeScript: load-and-call.ts
2// Fetch and instantiate a wasm module and call its exported `add` function.
3async function run() {
4 const response = await fetch('http://localhost:3000/simple.wasm');
5 // Use instantiateStreaming when available for efficiency.
6 const { instance } = await WebAssembly.instantiateStreaming(response, {});
7 // @ts-ignore
8 const result = instance.exports.add(2, 3);
9 console.log('2 + 3 =', result);
10}
11run().catch(console.error);- Fungsi-fungsi di dalam Wasm disimpan di
instance.exports. - Karena TypeScript tidak menerima informasi tipe, Anda perlu menggunakan
@ts-ignoreatau membuat definisi tipe sendiri.
Alur Kerja Menggunakan AssemblyScript
AssemblyScript memungkinkan Anda menulis Wasm dengan sintaks yang mirip dengan TypeScript, sehingga mudah diakses untuk pengembang TypeScript. Di sini, kita menyiapkan fungsi sederhana, membangunnya menjadi .wasm dan .d.ts, lalu memanggilnya dari TypeScript.
1// assembly/index.ts (AssemblyScript)
2// npm install --save-dev assemblyscript
3
4// Export a simple function to add two integers.
5export function add(a: i32, b: i32): i32 {
6 return a + b;
7}- Dengan menggunakan
asc(kompilerAssemblyScript), Anda dapat menghasilkan file.wasmdan, jika diinginkan, file definisi tipe.d.ts. Untuk mencobanya secara lokal, instalassemblyscriptdengan npm dan bangunlah.
1# build commands
2# npm install --save-dev assemblyscript
3npx asc assembly/index.ts -o build/simple.wasm -t build/simple.wat --bindings esm --exportTable --sourceMap
4
5# optionally generate d.ts with --exportRuntime or use as-bind / loader toolsBerikut adalah contoh pengambilan dan pemanggilan dari sisi TypeScript.
1// ts client that loads AssemblyScript-generated wasm
2async function runAssemblyScript() {
3 const res = await fetch('http://localhost:3000/build/simple.wasm');
4 const { instance } = await WebAssembly.instantiateStreaming(res, {});
5 // @ts-ignore
6 console.log('AssemblyScript add:', instance.exports.add(10, 7));
7}
8runAssemblyScript().catch(console.error);- AssemblyScript membutuhkan penanganan hati-hati untuk model memori dan string, tetapi sangat mudah digunakan untuk perhitungan numerik dasar.
Rust + wasm-bindgen (Pilihan yang kuat dan sering digunakan)
Bagian ini menjelaskan alur kerja penulisan Wasm dalam Rust dan menghubungkannya dengan JavaScript atau TypeScript menggunakan wasm-bindgen. Di sini, kami menggunakan fungsi Fibonacci sederhana sebagai contoh untuk menunjukkan cara mengimpor modul yang dihasilkan sebagai modul ES.
Ekspor fungsi dari sisi Rust menggunakan wasm-bindgen.
1// src/lib.rs (Rust)
2// install wasm-pack from https://drager.github.io/wasm-pack/installer/
3use wasm_bindgen::prelude::*;
4
5// Export a function to JavaScript using wasm-bindgen.
6#[wasm_bindgen]
7pub fn fib(n: u32) -> u32 {
8 if n <= 1 { return n; }
9 let mut a = 0;
10 let mut b = 1;
11 for _ in 2..=n {
12 let tmp = a + b;
13 a = b;
14 b = tmp;
15 }
16 b
17}- Saat Anda membangun dengan
wasm-packatau CLIwasm-bindgen, definisi tipe untuk TypeScript dan pelapis JS akan dibuat, sehingga Anda dapat langsung mengimpornya sebagai ESM.
1# build with wasm-pack
2# install wasm-pack from https://drager.github.io/wasm-pack/installer/
3wasm-pack build --target nodejs --out-dir pkgDari sisi TypeScript, impor dan gunakan modul ES dari pkg.
1// Node.js: import WASM module built with --target web
2// import init, { fib } from '../pkg/my_wasm_module.js';
3// Node.js: import WASM module built with --target nodejs
4import wasm from '../pkg/my_wasm_module.js';
5
6async function run() {
7 //await init(); // --target web
8 console.log('fib(10)=', wasm.fib(10));
9}
10
11run().catch(console.error);wasm-packmenghasilkan pelapis JavaScript dan definisi tipe.d.ts, sehingga mudah digunakan dari TypeScript. Harap diperhatikan bahwa ketika Anda menentukanwebuntuk opsi--targetpada perintahwasm-pack, inisialisasi secara asinkron diperlukan.
Contoh Dunia Nyata tentang Berbagi Memori: Mengoper dan Memproses Array (Tingkat Rendah)
Ketika mentransfer data dalam jumlah besar dengan Wasm, membagi ArrayBuffer untuk pertukaran data yang efisien sangatlah penting. Di sini kami menunjukkan contoh menggunakan AssemblyScript, tetapi prinsip yang sama berlaku untuk wasm-bindgen di Rust.
Dari sisi AssemblyScript, siapkan fungsi yang diekspor untuk menulis ke memori. Sebagai contoh, fungsi untuk mengkuadratkan setiap elemen array akan terlihat seperti ini.
1// assembly/array_ops.ts (AssemblyScript)
2// Square values in place in the wasm linear memory starting at `ptr` for `len` elements.
3export function square_in_place(ptr: usize, len: i32): void {
4 // Treat memory as a pointer to 32-bit integers.
5 for (let i = 0; i < len; i++) {
6 let offset = ptr + (i << 2); // i * 4 bytes
7 let value = load<i32>(offset);
8 store<i32>(offset, value * value);
9 }
10}Untuk menentukan pengaturan memori yang digunakan oleh AssemblyScript, siapkan asconfig.json berikut.
1{
2 "options": {
3 "memoryBase": 0,
4 "importMemory": false,
5 "initialMemory": 1,
6 "maximumMemory": 10
7 }
8}1 npx asc assembly/array_ops.ts -o build/array_ops.wasm -t build/array_ops.wat --bindings esm --exportTable --sourceMap- Untuk memanggil fungsi ini, Anda perlu menyalin
ArrayBufferke ruang memori Wasm dan mengoper pointer-nya.
Berikut adalah contoh menggunakan WebAssembly.Memory di TypeScript untuk menyalin data dan memanggil fungsi.
1// TypeScript: use memory to pass array to wasm
2async function runArrayOps() {
3 const res = await fetch('http://localhost:3000/build/array_ops.wasm');
4 const { instance } = await WebAssembly.instantiateStreaming(res, {});
5 // @ts-ignore
6 const memory: WebAssembly.Memory = instance.exports.memory;
7 // Create a view into wasm memory.
8 const i32View = new Int32Array(memory.buffer);
9
10 // Example data
11 const input = new Int32Array([1, 2, 3, 4]);
12 // Choose an offset (in i32 elements) to copy data to (simple example: at index 0).
13 const offset = 0;
14 i32View.set(input, offset);
15
16 // Call wasm function: ptr in bytes, len in elements
17 // @ts-ignore
18 instance.exports.square_in_place(offset * 4, input.length);
19
20 // Read back result
21 const result = i32View.slice(offset, offset + input.length);
22 console.log('squared:', result);
23}
24runArrayOps().catch(console.error);memory.bufferadalah memori linier bersama; meminimalkan penyalinan dapat meningkatkan kecepatan pemrosesan semaksimal mungkin. Perhatikan juga bahwa pointer mengacu pada posisi dalam byte, sementaraTypedArraydikelola berdasarkan jumlah elemen, jadi hati-hati untuk tidak tertukar.
Penanganan Aman Tipe: Siapkan Definisi Tipe TypeScript
Ekspor Wasm adalah objek JavaScript, sehingga menyediakan definisi tipe di sisi TypeScript akan mempermudah pengembangan. Berikut adalah contoh sederhana file definisi tipe.
Berikut menunjukkan definisi tipe minimal yang dapat Anda buat secara manual sebagai simple.d.ts.
1// simple.d.ts
2export function add(a: number, b: number): number;
3export const memory: WebAssembly.Memory;- Menempatkan ini di
typeRootspadatsconfig.jsonAnda atau menggunakandeclare moduleakan mengaktifkan pemeriksaan tipe.wasm-packsecara otomatis menghasilkan file.d.ts, jadi sangat berguna untuk menggunakannya.
Pola Inisialisasi pada Waktu Runtime: Sinkron vs Asinkron
Karena modul Wasm membutuhkan I/O (pengambilan) dan kompilasi, inisialisasi asinkron adalah hal yang umum. Namun, ada juga pola di mana Anda menyimpan cache WebAssembly.Module terlebih dahulu dan menginstansiasikannya secara sinkron.
Berikut adalah struktur kode dasar untuk inisialisasi WebAssembly secara asinkron. Dalam proyek nyata, pola ini sangat direkomendasikan.
1// async init pattern
2async function initWasm(url: string) {
3 const res = await fetch(url);
4 const { instance, module } = await WebAssembly.instantiateStreaming(res, {});
5 return instance;
6}- Inisialisasi asinkron memudahkan untuk menggabungkan penanganan kesalahan dan pemuatan malas secara fleksibel, sehingga paling nyaman dalam pengembangan nyata. Selain itu, kode yang dihasilkan oleh
wasm-packmencakup APIinit()untuk inisialisasi, sehingga terbiasa dengan alur ini akan membantu kelancaran pekerjaan Anda.
Pertimbangan Kinerja Praktis
Berikut adalah beberapa poin penting yang perlu diperhatikan untuk meningkatkan kinerja secara signifikan. Silakan merujuk ke tips optimasi ini saat mengombinasikan TypeScript dan WebAssembly.
- Ketika pemanggilan fungsi sangat sering, overhead panggilan antara JavaScript dan Wasm dapat menjadi hambatan. Kami menyarankan untuk mengelompokkan data dan memprosesnya sekaligus sebanyak mungkin.
- Alokasi dan penyalinan memori meningkatkan beban pemrosesan. Manfaatkan buffer dan pointer bersama untuk meminimalkan operasi tersebut.
- Berhati-hatilah saat menangani angka floating-point. Di TypeScript, mereka menjadi tipe
number, tetapi Anda dapat menangani secara akurat dengan mencocokkan tipe di sisi Wasm.
Ringkasan
Dengan mengombinasikan TypeScript dan WebAssembly, Anda dapat mencapai performa hampir setara native di browser. Ini sangat efektif untuk tugas yang memerlukan komputasi tinggi atau ketika Anda ingin memanfaatkan aset native yang sudah ada. Kombinasi ini adalah pilihan yang sangat kuat ketika Anda ingin meningkatkan kinerja aplikasi web Anda.
Anda dapat mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Silakan periksa juga saluran YouTube kami.