WebAssembly (WASM): запускаем C++ в браузере со скоростью «родного» кода

WebAssembly — это бинарный формат инструкций, который позволяет запускать код, написанный на C/C++, Rust, Go, C# и других языках, прямо в браузере со скоростью, близкой к нативной. При этом WASM работает в песочнице, как и JavaScript, но загружается и выполняется в 10–20 раз быстрее интерпретируемого JS.

Зачем нужен WASM, если есть JavaScript?
  • Высокие вычисления: фото- и видеофильтры, 3D-рендеринг, CAD, игры.
  • Портирование существующих библиотек: нет нужды переписывать миллионы строк C++ на JS.
  • Предсказуемая производительность: строгая типизация + компиляция AOT.
  • Кроссплатформенность: один бинарник работает на Windows, macOS, Linux, Android, iOS.

Как это работает
  • Пишем код на C++ (или Rust).
  • Компилируем в .wasm с помощью Emscripten (для C/C++) или wasm-pack (для Rust).
  • Получаем два файла: module.wasm и JS-обвязку для загрузки.
  • Используем WebAssembly API в браузере:
    Код:
      const { instance } = await WebAssembly.instantiateStreaming(fetch('module.wasm'));
      console.log(instance.exports.add(1, 2)); // 3

Минимальный пример: считаем факториал
C++:
Код:
extern "C" int fact(int n) {
    return n <= 1 ? 1 : n * fact(n - 1);
}

Компиляция:
Код:
emcc fact.cpp -O3 -s MODULARIZE=1 -s EXPORTED_FUNCTIONS='["_fact"]' -o fact.js

HTML:
Код:
<script type="module">
  import init, { fact } from './fact.js';
  await init();
  console.log('5! =', fact(5)); // 120
</script>

Граница JS ↔ WASM: память и FFI
  • Общая память — это один большой ArrayBuffer (WebAssembly.Memory).
  • Передаём только числа (i32, f64) или указатели в память.
  • Для строк/структур используем копирование в/из Linear Memory.
  • Скорость вызова: ~0.5 µs на переход, что в 5–10 раз дешевле JNI в Android.

Реальные кейсы
  • AutoCAD Web — полноценный CAD в браузере (порт 35 млн строк C++).
  • Unity WebGL — игровой движок компилируется в WASM.
  • Figma — движок рендеринга на C++ под капотом.
  • Photoshop для web — бетта уже на WASM + WebGL.

Сколько весит runtime
  • Минимальный модуль: 500 байт.
  • Emscripten runtime: +70 КБ gzip (malloc, libc, glue).
  • Rust wasm-pack: ~15 КБ gzip при --no-modules.

Ограничения
  • Нет прямого доступа к DOM (только через JS).
  • Нет потоков (SharedArrayBuffer + Atomics возвращаются, но нужны заголовки COOP/COEP).
  • Размер Linear Memory ограничен 4 ГБ (32-битные указатели).
  • Отладка: source-maps есть, но всё ещё проще gdb → printf.

Будущее
  • WebAssembly 2.0 — 64-битная память, SIMD, исключения, GC.
  • WASI — системный интерфейс для запуска WASM вне браузера (edge, IoT).
  • Component Model — универсальные ABI-библиотеки между языками.

Итого
WASM — это не замена JavaScript, а его «задняя передача» для тяжёлых вычислений. Если вашей задачи хватает JS — оставайтесь на нём. Если нужен Photoshop-уровень производительности — компилируйте в WASM и наслаждайтесь почти родной скоростью в любой вкладке.

Полезные ресурсы
 
Назад
Сверху