Хабрахабр

WebAssembly: что и как

Эта статья основана на моём выступлении на конференции ITSubbotnik, прошедшем 2 ноября 2019 года в Москве.

Вообще я бэкенд программист, но меня заинтересовала эта технология, она позволяет использовать мои знания бэкенда на фронте.

Проблема

Проблема эта — быстро исполнять код в браузере. Начнём с проблемы, которая решается этой (относительно новой) технологией. Быстро — это значит, «быстрее чем JavaScript», в идеале настолько быстро, насколько позволяет имеющийся у нас процессор.
Кроме того, исторически, вокруг этой проблемы постепенно возникли важные дополнительные требования:

  • Zero configuration — это должно быть решение «из коробки», ничего кроме браузера.
  • Безопасно — новая технология не должна создавать новых угроз, у нас и так есть чем заняться по части безопасности.
  • Кросс-платформенно — браузеры работают на всех основных процессорах, включая мобильные платформы.
  • Удобно для разработчиков — то есть для нас с вами.

История до Wasm

Можно сказать, что на этом поле есть победители и проигравшие. Мы видели множество вариантов исполнения кода в браузере.

Победители: это, безусловно, JavaScript; движок V8, сделавший JS таким быстрым; а также HTML5.

с безопасностью было очень плохо; Flash — сейчас мы наблюдаем эпоху заката Flash, хотя внутри него работает ActionScript, по сути, тот же JavaScript; Silverlight — наверное, он появился слишком поздно, чтобы занять серьёзную нишу.
В итоге, проиграли все плагины, в том числе из-за проблем с безопасностью. Проигравшие: ActiveX — если вы помните, эта технология позволяла делать с машиной вообще всё что угодно, т.е.

Были и другие попытки решения проблемы, уже в браузере:

  • NaCl — Native Client, предложенный Google; родной код прямо в браузере, что конечно ударяет по кросс-платформенности; Mozilla не поддержала эту инициативу, в итоге реализация была только в Chrome.
  • PNaCl — Portable Native Client — в качестве переносимого кода использовалось подмножество LLVM IR; также не было поддержано в Mozilla, опять же только Chrome. В итоге, Google отказалась от поддержки PNaCl в мае 2017 года.

asm.js

Появилась она в 2010 году, а в 2013 стала публично доступна. asm.js — ещё одна интересная инициатива, уже от Mozilla Foundation, которая подводит нас вплотную к теме WebAssembly.

asm.js это подмножество JavaScript, в него можно компилировать код из C и C++ с помощью компилятора Emscripten.

Кроме того, основные современные браузеры уже давно умеют быстро распознавать asm.js и эффективно компилировать его в родной код процессора. Поскольку это тоже JavaScript, то такой код будет исполняться в любом браузере. В сравнении с родным кодом, полученным непосредственно из C/C++, код полученный из asm.js медленнее всего в 1,5-2 раза (50-67 %).

Во-первых, мы здесь видим строку 'use asm', это маркер, дающий понять, что дальше идёт код на asm.js. Слева здесь код простейшей функции на C/C++, справа показано во что она превращается после компиляции в asm.js. Согласно спецификации, результатом этой операции является 32-разрядное целое со знаком. И в этом коде мы видим конструкции вида |0, это побитовая операция ИЛИ с нулевым значением. Тем не менее, такой тип возникает в результате этой операции, т.е. Но такого типа даже нет в JavaScript. это по сути это приведение значения к заданному типу.

В целом, asm.js это использование наших знаний о том, как браузерный движок компилирует JS, для того чтобы оптимизировать эту работу.

Что же такое WebAssembly

WebAssembly (или Wasm) — это бинарный формат, запускаемый в браузере, виртуальная машина, и результат компиляции с языка высокого уровня.

Wasm это не язык программирования, подобно тому как байт-код Java это не язык программирования, а результат компиляции и запускаемый блок кода.

Кто-то очень умный сказал, что название web assembly (то есть «ассемблер для веба») полностью неправильное, потому что это не ассемблер (не язык программирования) и он никак не связан с вебом (потому что это просто виртуальная машина).

видео Evolving Wasm into a proper misnomer: Andreas Rossberg. Создатели WebAssembly руководствовались следующими целями и ограничениями — см.

Но было ещё одно важное требование, это «продаваемость» — инициативу должны были воспринять и подхватить разработчики основных браузеров. По сути, они сводятся к трём вещам — кросс-платформенность, компактность, скорость. В итоге это удалось «продать», в разработке спецификации прияли участие представители Google, Mozilla, Microsoft и Apple.

Посмотрим, что представляет из себя Wasm как виртуальная машина.

Всего четыре типа данных: два целых, два плавающих. Это такой «выдуманный процессор», но без регистров, всё делается через стек. спецификацию и интерактивную таблицу. Относительно простой набор операций — см.

Здесь находится код, данные, константы, глобальные переменные, стек растущий вниз, куча растущая вверх. Плоская модель памяти: под память выделяется единый блок, размер которого кратен 64 КБ. Можно сделать так, чтобы куча автоматически увеличивалась при необходимости, при этом блок памяти расширяется на размер кратный 64 КБ.

Индекс 32-разрядный, поэтому адресуется до 4 ГБ памяти. Указатели не используется (это сделано для безопасности), вместо этого используется индекс.

Вся память WebAssembly полностью доступна из JavaScript, причём как на чтение, так и на запись.

Wasm всегда загружается и вызывается ТОЛЬКО из JavaScript. Посмотрим на модель исполнения WebAssembly. Более того, JS и Wasm работают в одной и той же «песочнице», и исполняются одним и тем же движком.

Это может быть вызов функции с передачей аргументов и возвратом значения, либо это может быть просто выполнение произвольной строки как JS-кода. Заметим, что из Wasm также можно вызывать JS.

Пробуем WebAssembly

Для того чтобы освоиться с WebAssembly, я рекомендую воспользоваться сайтом WasmFiddle или WebAssembly Studio — это простой и наглядный способ понять для себя, что такое Wasm на самом деле.

Когда мы нажимаем кнопку Build, то получаем блок кода Wasm (.wasm файл), который мы всегда можем развернуть в текстовое представление (.wat файл). Здесь слева вверху исходный код на C/C++ (в данном случае, рекурсивная функция расчёта числа Фибоначчи), справа вверху код на JS для загрузки Wasm, инстанциирования Wasm-модуля и вызова из него функции fib().

Ну и собственно скобочной записью это напоминает язык Lisp. Это текстовое представление с кучей скобочек — по сути, абстрактное синтаксическое дерево (AST). Но обычно мы получаем бинарный Wasm в результате компиляции с языка высокого уровня. По идее, мы можем редактировать код в виде текстового представления, и затем свернуть его вновь в бинарный формат — это напоминает программирование на языке ассемблера.

2017 год: Production Ready

Спецификация на все основные части Wasm была подготовлена, вышла реализация Wasm во всех основных браузерах. В ноябре 2017 года WebAssembly был объявлен «готовым к использованию в продакшене». 0, с которой мы и имеем дело сейчас. Тем самым, для WebAssembly был «выпущен» MVP — Minimum Viable Product, версия 1.

Поддержка в браузерах

В конце 2017 года были выпущены релизы всех основных браузеров с поддержкой WebAssembly:

Предполагалось, что для старых браузеров будет polyfill — возможность преобразования Wasm в asm.js; такие прототипы есть, но насколько я видел, эти проекты заброшены, видимо, сообществу не до них. Исключение — IE11, для него поддержки нет, и по всей видимости, уже не будет.

Сейчас среди всех установленных браузеров, ~88% поддерживают Wasm.

Поддержка языков

Сейчас уже довольно много языков поддерживают Wasm, и их становится всё больше с каждым месяцем. Для того, чтобы ваш любимый язык компилировался в Wasm, нужно чтобы компилятор обеспечивал такую цель компиляции. appcypher/awesome-wasm-langs. См.

  • C/C++ — через Emscripten, очень хорошая поддержка.
  • Rust — поддержка Wasm появилась довольно давно, экосистема вокруг Wasm строится во многом на основе Rust.
  • Java — через TeaVM, JWebAssembly, Bytecoder — на экспериментальном уровне.
  • Kotlin — есть поддержка в Kotlin/Native через LLVM backend — экспериментальная.
  • Go
  • C# — через Blazor (mono) и в будущем на Uno Platform.
  • TypeScript — через AssemblyScript.

Это облегчает работу для разработчиков языков программирования, мы получим ещё больше языков, компилирующих в Wasm. Не так давно появилась новость, что LLVM теперь поддерживает Wasm как цель компиляции.

Сценарии использования

Наверное, это один из главных вопросов — «А как я могу использовать Wasm?»

Уже сейчас WebAssembly активно применяется:

  • Игры, игровые движки, движки физики, VR/AR — например, Godot, Doom 3
  • Эмуляторы, виртуальные машины — например, DOSBox
  • Графические/3D редакторы — Figma, AutoCAD
  • Веб-клиенты для финансовых торговых площадок — я видел презентации про два таких клиента
  • Кодеки и фильтры аудио/видео — например, ffmpeg
  • Базы данных — например, sqlite

Другие возможные сценарии:

  • Progressive web applications (PWA)
  • Распознавание натренированной нейронной сетью

Производительность

Производительность Wasm была одним из его главных «продающих» факторов, но что с ней происходит на самом деле?

Также это может сильно зависеть от используемого браузера. В сравнении с JavaScript, получается, что в среднем Wasm быстрее, но в каждом частном случае нужно делать сравнение JS/Wasm, потому что может получиться и во много раз лучше, и в несколько раз хуже.

Но JS гораздо легче теряет в производительности, а Wasm обеспечивает более «ровный» подход. На самом деле, пиковая производительность JS и Wasm одинакова, поскольку оба в итоге превращаются в родной код процессора.

Там где много операций с памятью, Wasm проигрывает. Как правило, Wasm хорошо показывает себя на объёмных вычислениях. См. Ну и основная проблема в реальных применениях — это медленный интероп JS <-> Wasm. например, бенчмарк.

Native Code». В июле 2019 года вышла научная статья «Not So Fast: Analyzing the Performance of WebAssembly vs. Авторы реализовали возможность запуска под WebAssembly консольных утилит Linux, для запуска бенчмарков, и использовали бенчмарки SPEC для оценки производительности Wasm по сравнению с теми же тестами на asm.js и на родном коде.

Результаты такие:

  • Wasm на 30% быстрее, чем JavaScript (в среднем, на этих тестах)
  • Wasm на 50% медленнее, чем родной код (в среднем, на этих тестах)

Авторы статьи также дали анализ причин, на чём именно Wasm «подтормаживает»:

  • примерно вдвое больше операций загрузки/сохранения данных, по сравнению с родным кодом;
  • больше ветвлений — вызвано необходимостью дополнительных проверок при обращении к памяти;
  • больше «промахов» мимо кеша L1.

К тому же, этот анализ позволит разработчикам браузеров сделать Wasm ещё быстрее. В общем, на самом деле, с производительностью всё не так плохо.

В будущем нас ожидает ускорение Wasm не только за счёт лучшей оптимизации в браузерах, но и за счёт новых фич, таких как: блоковые операции над памятью, поддержка SIMD-инструкций, поддержка threads.

Что будет дальше?

Как развивается WebAssembly?

Спецификации находятся на разных этапах (фазах), есть определённая «дорожная карта» этой работы. Во-первых, группа, работающая над спецификациями для Wasm, продолжает свою работу.

В частности, в ближайшее время мы ожидаем дальше увидеть такие фичи:

  • Non-trapping float-to-int conversions — сейчас конвертация из плавающего значения в целое в некоторых условиях может вызвать исключение; для программиста это довольно неожиданно, поэтому все такие конвертации приходится оборачивать, теряя на этом производительность. Эта фича решает проблему.
  • Multi-value — возможность возврата больше одного значения из функции, возможность создания новых инструкций, возвращающих более одного значения — например, результат деления и остаток от деления, или результат сложения и бит переноса.
  • Reference Types — введение типа anyref, обозначающего «ссылка на что-то в куче JS», первый шаг к тому чтобы ускорить взаимодействие с JS.

постепенно к Wasm добавляются новые фичи, сначала скрытые «под флагом» в настройках, а затем и включенные по умолчанию. Во-вторых, разработчики браузеров, со своей стороны, реализуют эти спецификации, т.е.

Вот, например, список фич Chrome, относящихся к WebAssembly.
Для Firefox подобный список можно найти здесь.

WebAssembly вне браузера

А значит, его вполне можно использовать и вне веба. Как было сказано выше, Wasm по сути никак не связан с вебом, это просто виртуальная машина.

Сейчас видны несколько сценариев использования Wasm вне браузера:

  • Node.js — в основе Node.js лежит движок V8, который поддерживает Wasm.
  • Отдельное консольное приложение, код приложения исполняется в Wasm VM — примеры таких runtime: wasmtime, wasmer.
  • Wasm VM используется как библиотека из других языков — например, wasmer позволяет вызывать себя из десятка различных языков.

Это привело к созданию WebAssembly System Interface (WASI) — спецификации кросс-платформенного API, подобного POSIX. Для Wasm работающего вне браузера, уже не нужны ограничения «песочницы», напротив, необходим доступ к функциям системы — файловая система и файлы, консольный ввод/вывод и т.д. WebAssembly/WASI и wasi.dev. См.

Здесь вы можете взять готовый .wasm-файл и использовать его в своём приложении. Следующим шагом стало создание менеджера пакетов — Wasm Package Manager (WAPM) — warp.io. Часть пакетов помечено тэгом «WASI», что означает — их можно использовать только в сценариях работы вне браузера. Обычно тут речь идёт о Wasm-версиях каких-то известных библиотек.

Заключение

Итак, WebAssembly вполне можно использовать, он уже два года как «production ready».
Применение Wasm вполне может дать некоторое ускорение, по сравнению с аналогичным кодом на JavaScript, но всегда нужно проверять, получился ли прирост скорости.
Поддержка Wasm со стороны языков программирования постоянно развивается.

Ну и самое главное, WebAssembly несколько «изменил ландшафт» веба — предоставил нам новые сценарии использования, которые мы можем реализовать в своих приложениях.

Ссылки

Awesome списки:

Видео:

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть