- PVSM.RU - https://www.pvsm.ru -
Эта статья основана на моём выступлении на конференции ITSubbotnik, прошедшем 2 ноября 2019 года в Москве.
Вообще я бэкенд программист, но меня заинтересовала эта технология, она позволяет использовать мои знания бэкенда на фронте.
Начнём с проблемы, которая решается этой (относительно новой) технологией. Проблема эта — быстро исполнять код в браузере. Быстро — это значит, «быстрее чем JavaScript», в идеале настолько быстро, насколько позволяет имеющийся у нас процессор.
Кроме того, исторически, вокруг этой проблемы постепенно возникли важные дополнительные требования:
Мы видели множество вариантов исполнения кода в браузере. Можно сказать, что на этом поле есть победители и проигравшие.
Победители: это, безусловно, JavaScript; движок V8, сделавший JS таким быстрым; а также HTML5.
Проигравшие: ActiveX — если вы помните, эта технология позволяла делать с машиной вообще всё что угодно, т.е. с безопасностью было очень плохо; Flash — сейчас мы наблюдаем эпоху заката Flash, хотя внутри него работает ActionScript, по сути, тот же JavaScript; Silverlight — наверное, он появился слишком поздно, чтобы занять серьёзную нишу.
В итоге, проиграли все плагины, в том числе из-за проблем с безопасностью.
Были и другие попытки решения проблемы, уже в браузере:
asm.js — ещё одна интересная инициатива, уже от Mozilla Foundation, которая подводит нас вплотную к теме WebAssembly. Появилась она в 2010 году, а в 2013 стала публично доступна.
asm.js это подмножество JavaScript, в него можно компилировать код из C и C++ с помощью компилятора Emscripten.
Поскольку это тоже JavaScript, то такой код будет исполняться в любом браузере. Кроме того, основные современные браузеры уже давно умеют быстро распознавать asm.js и эффективно компилировать его в родной код процессора. В сравнении с родным кодом, полученным непосредственно из C/C++, код полученный из asm.js медленнее всего в 1,5-2 раза (50-67 %).
Слева здесь код простейшей функции на C/C++, справа показано во что она превращается после компиляции в asm.js. Во-первых, мы здесь видим строку 'use asm'
, это маркер, дающий понять, что дальше идёт код на asm.js. И в этом коде мы видим конструкции вида |0
, это побитовая операция ИЛИ с нулевым значением. Согласно спецификации, результатом этой операции является 32-разрядное целое со знаком. Но такого типа даже нет в JavaScript. Тем не менее, такой тип возникает в результате этой операции, т.е. это по сути это приведение значения к заданному типу.
В целом, asm.js это использование наших знаний о том, как браузерный движок компилирует JS, для того чтобы оптимизировать эту работу.
WebAssembly (или Wasm) — это бинарный формат, запускаемый в браузере, виртуальная машина, и результат компиляции с языка высокого уровня.
Wasm это не язык программирования, подобно тому как байт-код Java это не язык программирования, а результат компиляции и запускаемый блок кода.
Кто-то очень умный сказал, что название web assembly (то есть «ассемблер для веба») полностью неправильное, потому что это не ассемблер (не язык программирования) и он никак не связан с вебом (потому что это просто виртуальная машина).
Создатели WebAssembly руководствовались следующими целями и ограничениями — см. видео Evolving Wasm into a proper misnomer: Andreas Rossberg [1].
По сути, они сводятся к трём вещам — кросс-платформенность, компактность, скорость. Но было ещё одно важное требование, это «продаваемость» — инициативу должны были воспринять и подхватить разработчики основных браузеров. В итоге это удалось «продать», в разработке спецификации прияли участие представители Google, Mozilla, Microsoft и Apple.
Посмотрим, что представляет из себя Wasm как виртуальная машина.
Это такой «выдуманный процессор», но без регистров, всё делается через стек. Всего четыре типа данных: два целых, два плавающих. Относительно простой набор операций — см. спецификацию [2] и интерактивную таблицу [3].
Плоская модель памяти: под память выделяется единый блок, размер которого кратен 64 КБ. Здесь находится код, данные, константы, глобальные переменные, стек растущий вниз, куча растущая вверх. Можно сделать так, чтобы куча автоматически увеличивалась при необходимости, при этом блок памяти расширяется на размер кратный 64 КБ.
Указатели не используется (это сделано для безопасности), вместо этого используется индекс. Индекс 32-разрядный, поэтому адресуется до 4 ГБ памяти.
Вся память WebAssembly полностью доступна из JavaScript, причём как на чтение, так и на запись.
Посмотрим на модель исполнения WebAssembly. Wasm всегда загружается и вызывается ТОЛЬКО из JavaScript. Более того, JS и Wasm работают в одной и той же «песочнице», и исполняются одним и тем же движком.
Заметим, что из Wasm также можно вызывать JS. Это может быть вызов функции с передачей аргументов и возвратом значения, либо это может быть просто выполнение произвольной строки как JS-кода.
Для того чтобы освоиться с WebAssembly, я рекомендую воспользоваться сайтом WasmFiddle [4] или WebAssembly Studio [5] — это простой и наглядный способ понять для себя, что такое Wasm на самом деле.
Здесь слева вверху исходный код на C/C++ (в данном случае, рекурсивная функция расчёта числа Фибоначчи), справа вверху код на JS для загрузки Wasm, инстанциирования Wasm-модуля и вызова из него функции fib()
. Когда мы нажимаем кнопку Build, то получаем блок кода Wasm (.wasm файл), который мы всегда можем развернуть в текстовое представление (.wat файл).
Это текстовое представление с кучей скобочек — по сути, абстрактное синтаксическое дерево (AST). Ну и собственно скобочной записью это напоминает язык Lisp. По идее, мы можем редактировать код в виде текстового представления, и затем свернуть его вновь в бинарный формат — это напоминает программирование на языке ассемблера. Но обычно мы получаем бинарный Wasm в результате компиляции с языка высокого уровня.
В ноябре 2017 года WebAssembly был объявлен «готовым к использованию в продакшене». Спецификация на все основные части Wasm была подготовлена, вышла реализация Wasm во всех основных браузерах. Тем самым, для WebAssembly был «выпущен» MVP — Minimum Viable Product, версия 1.0, с которой мы и имеем дело сейчас.
В конце 2017 года были выпущены релизы всех основных браузеров с поддержкой WebAssembly:
Исключение — IE11, для него поддержки нет, и по всей видимости, уже не будет. Предполагалось, что для старых браузеров будет polyfill — возможность преобразования Wasm в asm.js; такие прототипы есть, но насколько я видел, эти проекты заброшены, видимо, сообществу не до них.
Сейчас среди всех установленных браузеров, ~88% поддерживают Wasm [6].
Для того, чтобы ваш любимый язык компилировался в Wasm, нужно чтобы компилятор обеспечивал такую цель компиляции. Сейчас уже довольно много языков поддерживают Wasm, и их становится всё больше с каждым месяцем. См. appcypher/awesome-wasm-langs [7].
Не так давно появилась новость, что LLVM теперь поддерживает Wasm как цель компиляции. Это облегчает работу для разработчиков языков программирования, мы получим ещё больше языков, компилирующих в Wasm.
Наверное, это один из главных вопросов — «А как я могу использовать Wasm?»
Уже сейчас WebAssembly активно применяется:
Другие возможные сценарии:
Производительность Wasm была одним из его главных «продающих» факторов, но что с ней происходит на самом деле?
В сравнении с JavaScript, получается, что в среднем Wasm быстрее, но в каждом частном случае нужно делать сравнение JS/Wasm, потому что может получиться и во много раз лучше, и в несколько раз хуже. Также это может сильно зависеть от используемого браузера.
На самом деле, пиковая производительность JS и Wasm одинакова, поскольку оба в итоге превращаются в родной код процессора. Но JS гораздо легче теряет в производительности, а Wasm обеспечивает более «ровный» подход.
Как правило, Wasm хорошо показывает себя на объёмных вычислениях. Там где много операций с памятью, Wasm проигрывает. Ну и основная проблема в реальных применениях — это медленный интероп JS <-> Wasm. См. например, бенчмарк [18].
В июле 2019 года вышла научная статья «Not So Fast: Analyzing the Performance of WebAssembly vs. Native Code» [19]. Авторы реализовали возможность запуска под WebAssembly консольных утилит Linux, для запуска бенчмарков, и использовали бенчмарки SPEC для оценки производительности Wasm по сравнению с теми же тестами на asm.js и на родном коде.
Результаты такие:
Авторы статьи также дали анализ причин, на чём именно Wasm «подтормаживает»:
В общем, на самом деле, с производительностью всё не так плохо. К тому же, этот анализ позволит разработчикам браузеров сделать Wasm ещё быстрее.
В будущем нас ожидает ускорение Wasm не только за счёт лучшей оптимизации в браузерах, но и за счёт новых фич, таких как: блоковые операции над памятью, поддержка SIMD-инструкций, поддержка threads.
Как развивается WebAssembly?
Во-первых, группа, работающая над спецификациями для Wasm, продолжает свою работу. Спецификации находятся на разных этапах (фазах), есть определённая «дорожная карта» [20] этой работы.
В частности, в ближайшее время мы ожидаем дальше увидеть такие фичи:
Во-вторых, разработчики браузеров, со своей стороны, реализуют эти спецификации, т.е. постепенно к Wasm добавляются новые фичи, сначала скрытые «под флагом» в настройках, а затем и включенные по умолчанию.
Вот, например, список фич Chrome, относящихся к WebAssembly [21].
Для Firefox подобный список можно найти здесь [22].
Как было сказано выше, Wasm по сути никак не связан с вебом, это просто виртуальная машина. А значит, его вполне можно использовать и вне веба.
Сейчас видны несколько сценариев использования Wasm вне браузера:
Для Wasm работающего вне браузера, уже не нужны ограничения «песочницы», напротив, необходим доступ к функциям системы — файловая система и файлы, консольный ввод/вывод и т.д. Это привело к созданию WebAssembly System Interface (WASI) — спецификации кросс-платформенного API, подобного POSIX. См. WebAssembly/WASI [25] и wasi.dev [26].
Следующим шагом стало создание менеджера пакетов — Wasm Package Manager (WAPM) — warp.io [27]. Здесь вы можете взять готовый .wasm-файл и использовать его в своём приложении. Обычно тут речь идёт о Wasm-версиях каких-то известных библиотек. Часть пакетов помечено тэгом «WASI», что означает — их можно использовать только в сценариях работы вне браузера.
Итак, WebAssembly вполне можно использовать, он уже два года как «production ready».
Применение Wasm вполне может дать некоторое ускорение, по сравнению с аналогичным кодом на JavaScript, но всегда нужно проверять, получился ли прирост скорости.
Поддержка Wasm со стороны языков программирования постоянно развивается.
Ну и самое главное, WebAssembly несколько «изменил ландшафт» веба — предоставил нам новые сценарии использования, которые мы можем реализовать в своих приложениях.
Awesome списки:
Видео:
Автор: nzeemin
Источник [38]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/336594
Ссылки в тексте:
[1] Evolving Wasm into a proper misnomer: Andreas Rossberg: https://www.youtube.com/watch?v=pq-Pa2Fj4nE
[2] спецификацию: https://webassembly.github.io/spec/core/appendix/index-instructions.html
[3] интерактивную таблицу: https://pengowray.github.io/wasm-ops/
[4] WasmFiddle: https://wasdk.github.io/WasmFiddle/
[5] WebAssembly Studio: https://webassembly.studio/
[6] ~88% поддерживают Wasm: https://caniuse.com/#feat=wasm
[7] appcypher/awesome-wasm-langs: https://github.com/appcypher/awesome-wasm-langs
[8] Emscripten: https://emscripten.org/
[9] Blazor: http://blazor.net/
[10] Uno Platform: https://platform.uno/
[11] AssemblyScript: https://docs.assemblyscript.org/
[12] Godot: https://godotengine.org/
[13] Doom 3: https://wasm.continuation-labs.com/d3demo/
[14] DOSBox: https://github.com/dreamlayers/em-dosbox
[15] Figma: https://www.figma.com/blog/webassembly-cut-figmas-load-time-by-3x/
[16] AutoCAD: https://www.infoq.com/presentations/autocad-webassembly/
[17] ffmpeg: https://habr.com/ru/post/473098/
[18] бенчмарк: https://takahirox.github.io/WebAssembly-benchmark/
[19] «Not So Fast: Analyzing the Performance of WebAssembly vs. Native Code»: https://www.usenix.org/system/files/atc19-jangda.pdf
[20] «дорожная карта»: https://github.com/WebAssembly/proposals
[21] список фич Chrome, относящихся к WebAssembly: https://www.chromestatus.com/features#webassembly
[22] здесь: https://developer.mozilla.org/en-US/docs/WebAssembly
[23] wasmtime: https://github.com/bytecodealliance/wasmtime
[24] wasmer: https://github.com/wasmerio/wasmer
[25] WebAssembly/WASI: https://github.com/WebAssembly/WASI
[26] wasi.dev: https://wasi.dev/
[27] warp.io: https://wapm.io/
[28] webassembly.org: https://webassembly.org/
[29] github.com/webassembly: https://github.com/webassembly
[30] mbasso/awesome-wasm: https://github.com/mbasso/awesome-wasm
[31] appcypher/awesome-wasm-runtimes: https://github.com/appcypher/awesome-wasm-runtimes
[32] WebAssembly for Web Developers (Google I/O ’19): https://www.youtube.com/watch?v=njt-Qzw0mVY
[33] What is WebAssembly? By Some of its Creators: https://www.youtube.com/watch?v=fvkIQfRZ-Y0
[34] C++ Russia 2018: Павел Булатов, Переход на WebAssembly: стоит ли игра свеч?: https://www.youtube.com/watch?v=hGaZ0gr1bh8
[35] Андрей Нагих — Разработка под WebAssembly: реальные грабли и примеры: https://www.youtube.com/watch?v=uqG9DiT80UE
[36] Сергей Рубанов — Понятно о WebAssembly: https://www.youtube.com/watch?v=Q96VSAsxEtU
[37] WebAssembly in the Browser and Beyond by Dan Callahan | Mozilla Developer Roadshow EU 2019: https://www.youtube.com/watch?v=RUAXPOSVV1A
[38] Источник: https://habr.com/ru/post/475778/?utm_source=habrahabr&utm_medium=rss&utm_campaign=475778
Нажмите здесь для печати.