Deteksi fitur WebAssembly

Pelajari cara menggunakan fitur WebAssembly terbaru sambil 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 umumnya fitur baru di web, urutan penerapan dan linimasanya dapat berbeda secara signifikan di antara mesin yang berbeda. Jika ingin menggunakan fitur baru tersebut, Anda harus memastikan bahwa tidak ada pengguna yang terabaikan. Dalam artikel ini, Anda akan mempelajari pendekatan untuk mencapainya.

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

Anda dapat menemukan daftar lengkap proposal dan masing-masing tahapannya di repo resmi atau melacak status penerapannya di mesin pada halaman panduan fitur resmi.

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

Fitur pemilihan dan pengelompokan

Mari kita pelajari langkah-langkah tersebut dengan memilih beberapa set fitur arbitrer sebagai contoh. Anggap saja 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 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 lain: hanya mengasumsikan dukungan WebAssembly dasar.

Perincian ini dibagi berdasarkan dukungan fitur dalam versi terbaru setiap browser. Browser modern selalu diperbarui dan diperbarui secara otomatis, sehingga Anda biasanya hanya perlu mengkhawatirkan rilis terbaru. Namun, selama Anda menyertakan WebAssembly dasar pengukuran sebagai kelompok penggantian, Anda tetap dapat menyediakan aplikasi yang berfungsi bahkan untuk pengguna dengan browser yang sudah tidak update.

Mengompilasi berbagai set fitur

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

Setiap toolchain dan sistem build berbeda, dan Anda harus membaca dokumentasi compiler sendiri untuk mengetahui cara menyesuaikan fitur tersebut. Demi kemudahan, saya akan menggunakan library C++ file tunggal dalam contoh berikut dan menunjukkan cara mengompilasinya 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. Konfigurasinya 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 dasar yang dipilih melalui flag kompilasi.

Memuat paket yang benar

Setelah membuat paket untuk semua kohor fitur, Anda harus memuat paket yang benar dari aplikasi JavaScript utama. Untuk melakukannya, pertama-tama, deteksi fitur mana yang didukung di browser saat ini. Anda dapat melakukannya dengan library wasm-feature-detect. Dengan menggabungkannya bersama impor dinamis, Anda dapat memuat paket yang paling dioptimalkan di browser apa 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, membangun, dan beralih antar-paket untuk set fitur yang berbeda.

Seiring bertambahnya jumlah fitur,jumlah kelompok fitur mungkin tidak lagi dapat dipertahankan. 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 dapat 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 implementasi yang berbeda dari fungsi yang sama dalam modul. Namun, mekanisme seperti itu sendiri 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 membangun dan memuat kode menggunakan fitur WebAssembly baru di semua browser.