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 yang biasanya terjadi pada fitur baru di web, urutan dan jadwal implementasinya dapat berbeda secara signifikan di antara mesin yang berbeda. Jika Anda 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 tahapannya masing-masing di repositori resmi atau melacak status penerapannya di mesin telusur di halaman roadmap fitur resmi.

Untuk memastikan bahwa pengguna semua browser dapat menggunakan aplikasi Anda, Anda harus mencari tahu fitur mana 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 set fitur arbitrer sebagai contoh. Katakanlah saya telah mengidentifikasi bahwa saya ingin menggunakan SIMD, thread, dan penanganan pengecualian di library saya karena alasan ukuran dan performa. Dukungan browsernya 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 bahwa 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 didukung.
  • Safari: Thread didukung, SIMD dan penanganan pengecualian tidak didukung.
  • Browser lain: hanya gunakan dukungan WebAssembly dasar.

Pengelompokan 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 sebagai kelompok fallback, Anda masih dapat menyediakan aplikasi yang berfungsi bahkan untuk pengguna dengan browser yang sudah usang.

Mengompilasi set fitur yang berbeda

WebAssembly tidak memiliki cara bawaan untuk mendeteksi fitur yang didukung dalam runtime, oleh karena itu semua petunjuk dalam modul harus didukung pada 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. Agar lebih praktis, 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 secara kondisional memilih antara implementasi paralel (thread dan SIMD) dari fungsi yang sama dan implementasi serial pada waktu kompilasi. Kodenya 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 kelompok fitur, Anda harus memuat kelompok fitur 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 akhir

Dalam postingan ini, saya telah menunjukkan cara memilih, membangun, dan beralih di antara paket untuk set fitur yang berbeda.

Seiring bertambahnya jumlah fitur,jumlah kelompok fitur mungkin menjadi tidak 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 bisa memberikan keseimbangan yang wajar antara progressive enhancement dan performa runtime.

Di masa mendatang, WebAssembly mungkin akan mendapatkan cara bawaan untuk mendeteksi fitur yang didukung dan beralih di antara implementasi yang berbeda dari fungsi yang sama dalam modul. Namun, mekanisme tersebut akan menjadi fitur pasca-MVP yang perlu Anda deteksi dan muat secara kondisional 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.