Deteksi fitur WebAssembly

Pelajari cara menggunakan fitur WebAssembly terbaru sekaligus mendukung pengguna di semua browser.

WebAssembly 1.0 dirilis empat tahun lalu, tetapi pengembangan tidak berhenti di situ. Fitur baru ditambahkan melalui proses standardisasi proposal. Seperti yang biasanya terjadi pada fitur baru di web, urutan dan jadwal implementasinya dapat berbeda secara signifikan di antara mesin yang berbeda. Jika ingin menggunakan fitur baru tersebut, Anda harus memastikan bahwa tidak ada pengguna yang tertinggal. Dalam artikel ini, Anda akan mempelajari pendekatan untuk mencapai hal tersebut.

Sebagian fitur baru meningkatkan ukuran kode dengan menambahkan petunjuk baru untuk operasi umum, sebagian lagi menambahkan dasar performa yang canggih, dan yang lainnya meningkatkan pengalaman developer dan integrasi dengan seluruh web.

Anda dapat menemukan daftar lengkap proposal dan tahapnya masing-masing di repo resmi atau melacak status penerapannya di mesin di halaman peta jalan fitur resmi.

Untuk memastikan bahwa pengguna dari semua browser dapat menggunakan aplikasi Anda, Anda perlu mengetahui fitur yang ingin digunakan. Kemudian, bagi menjadi beberapa kelompok berdasarkan dukungan browser. Kemudian, kompilasi codebase Anda secara terpisah untuk setiap grup tersebut. Terakhir, di sisi browser, Anda perlu mendeteksi fitur yang didukung dan memuat paket JavaScript dan Wasm yang sesuai.

Fitur memilih dan pengelompokan

Mari kita pelajari langkah-langkah tersebut dengan memilih beberapa kumpulan fitur arbitrer sebagai contoh. Misalnya, saya telah mengidentifikasi bahwa saya ingin menggunakan SIMD, thread, dan penanganan pengecualian di library saya karena alasan ukuran dan performa. Dukungan browser mereka adalah sebagai berikut:

Tabel yang menunjukkan dukungan browser untuk fitur yang dipilih.
Lihat tabel fitur ini di webassembly.org/roadmap.

Anda dapat membagi browser ke dalam beberapa kelompok berikut untuk memastikan setiap pengguna mendapatkan pengalaman yang paling optimal:

  • Browser berbasis Chrome: Thread, SIMD, dan penanganan pengecualian semuanya didukung.
  • Firefox: Thread dan SIMD didukung, penanganan pengecualian tidak.
  • Safari: Thread didukung, SIMD dan penanganan pengecualian tidak didukung.
  • Browser lainnya: hanya mengasumsikan dukungan WebAssembly dasar.

Perincian ini dibagi menurut dukungan fitur di versi terbaru setiap browser. Browser modern selalu aktual dan diupdate secara otomatis, jadi dalam kebanyakan kasus, Anda hanya perlu memikirkan rilis terbaru. Namun, selama Anda menyertakan WebAssembly dasar pengukuran sebagai kohor penggantian, Anda tetap dapat menyediakan aplikasi yang berfungsi bahkan untuk pengguna dengan browser yang sudah usang.

Mengompilasi untuk berbagai kumpulan fitur

WebAssembly tidak memiliki cara bawaan untuk mendeteksi fitur yang didukung dalam runtime, sehingga semua petunjuk dalam modul harus didukung di target. Oleh karena itu, Anda harus mengompilasi kode sumber ke Wasm secara terpisah untuk setiap set fitur yang berbeda tersebut.

Setiap toolchain dan sistem build berbeda, dan Anda harus membaca dokumentasi compiler Anda sendiri untuk mengetahui cara menyesuaikan fitur tersebut. Untuk mempermudah, saya akan menggunakan library C++ satu file dalam contoh berikut dan menunjukkan cara mengompilasi dengan Emscripten.

Saya akan menggunakan SIMD melalui emulasi SSE2, thread melalui dukungan library Pthreads, dan memilih antara penanganan pengecualian Wasm dan implementasi JavaScript penggantian:

# First bundle: threads + SIMD + Wasm exceptions
$ emcc main.cpp -o main.threads-simd-exceptions.mjs -pthread -msimd128 -msse2 -fwasm-exceptions
# Second bundle: threads + SIMD + JS exceptions fallback
$ emcc main.cpp -o main.threads-simd.mjs -pthread -msimd128 -msse2 -fexceptions
# Third bundle: threads + JS exception fallback
$ emcc main.cpp -o main.threads.mjs -pthread -fexceptions
# Fourth bundle: basic Wasm with JS exceptions fallback
$ emcc main.cpp -o main.basic.mjs -fexceptions

Kode C++ itu sendiri dapat menggunakan #ifdef __EMSCRIPTEN_PTHREADS__ dan #ifdef __SSE2__ untuk memilih secara kondisional antara implementasi paralel (thread dan SIMD) dari fungsi yang sama dan implementasi serial pada waktu kompilasi. Tampilannya akan terlihat seperti ini:

void process_data(std::vector<int>& some_input) {
#ifdef __EMSCRIPTEN_PTHREADS__
#ifdef __SSE2__
  // …implementation using threads and SIMD for max speed
#else
  // …implementation using threads but not SIMD
#endif
#else
  // …fallback implementation for browsers without those features
#endif
}

Penanganan pengecualian tidak memerlukan perintah #ifdef, karena dapat digunakan dengan cara yang sama dari C++, terlepas dari implementasi yang mendasarinya yang dipilih melalui flag kompilasi.

Memuat paket yang benar

Setelah membuat paket untuk semua kelompok fitur, Anda perlu memuat paket yang benar dari aplikasi JavaScript utama. Untuk melakukannya, pertama-tama, deteksi fitur apa yang didukung di browser saat ini. Anda dapat melakukannya dengan library wasm-feature-detect. Dengan menggabungkannya dengan impor dinamis, Anda dapat memuat paket yang paling dioptimalkan di browser mana pun:

import { simd, threads, exceptions } from 'https://unpkg.com/wasm-feature-detect?module';

let initModule;
if (await threads()) {
  if (await simd()) {
    if (await exceptions()) {
      initModule = import('./main.threads-simd-exceptions.mjs');
    } else {
      initModule = import('./main.threads-simd.mjs');
    }
  } else {
    initModule = import('./main.threads.mjs');
  }
} else {
  initModule = import('./main.basic.mjs');
}

const Module = await initModule();
// now you can use `Module` Emscripten object like you normally would

Kata terakhir

Dalam postingan ini, saya telah menunjukkan cara memilih, mem-build, dan beralih antar-paket untuk berbagai set fitur.

Seiring bertambahnya jumlah fitur, jumlah kohor fitur mungkin tidak dapat dikelola. Untuk mengatasi masalah ini, Anda dapat memilih kelompok fitur berdasarkan data pengguna di dunia nyata, melewati browser yang kurang populer dan membiarkannya kembali ke kelompok yang sedikit kurang optimal. Selama aplikasi Anda masih berfungsi untuk semua pengguna, pendekatan ini bisa memberikan keseimbangan yang wajar antara progressive enhancement dan performa runtime.

Di masa mendatang, WebAssembly mungkin mendapatkan cara bawaan untuk mendeteksi fitur yang didukung dan beralih di antara berbagai implementasi fungsi yang sama dalam modul. Namun, mekanisme tersebut akan menjadi fitur pasca-MVP yang perlu Anda deteksi dan muat secara bersyarat menggunakan pendekatan di atas. Hingga saat itu, pendekatan ini tetap menjadi satu-satunya cara untuk mem-build dan memuat kode menggunakan fitur WebAssembly baru di semua browser.