Web assembly что это
Введение в WebAssembly: как устроена технология и почему она важна
Авторизуйтесь
Введение в WebAssembly: как устроена технология и почему она важна
На практике WebAssembly реализуется разработчиками браузеров на основе существующего JavaScript-движка. По сути, он предназначен для замены JavaScript как целевого языка. Например, вместо компиляции TypeScript в JavaScript его разработчики теперь могут компилировать свой код в WebAssembly. Иными словами, это не новая виртуальная машина, это новый формат для той же самой виртуальной машины JavaScript, которая включена в каждый браузер. Это позволит использовать существующую инфраструктуру JavaScript без использования самого JavaScript.
Разработка MVP была завершена в марте 2017, и сейчас есть готовые реализации для всех основных браузеров.
Почему это важно?
Во-первых, новый формат WebAssembly обещает значительное увеличение производительности парсинга. Как сказано в FAQ WebAssembly, тип бинарного формата, используемый в WebAssembly, может быть декодирован гораздо быстрее, чем JavaScript может быть пропарсен (эксперименты показывают более чем 20-кратную разницу). На мобильных устройствах большому скомпилированному коду может запросто потребоваться 20–40 секунд только на парсинг, поэтому встроенное декодирование (особенно в сочетании с такими технологиями, как улучшенная по сравнению с gzip потоковая подача для сжатия) имеет решающее значение для обеспечения хорошего пользовательского опыта.
Обратите внимание, что речь идёт о производительности парсинга, а не о производительности исполнения, т.к. во многих случаях будет использоваться существующий JavaScript-движок. Однако даже это позволит использовать в вебе ПО, которое раньше было бы нецелесообразно разрабатывать, например: виртуальные машины, виртуальную реальность, распознавание изображений и многое другое.
Первыми пользователями wasm, вероятно, будут разработчики игровых движков, поскольку они всё время ищут, как бы улучшить производительность. До WebAssembly лучшим, на что они могли надеяться, был asm.js (упрощённый JavaScript, оптимизированный для скорости), который был хорошей технологией, но не очень полезной для многих игр.
Прим.перев. На самом деле, Rust уже умеет компилировать в wasm напрямую, без Emscipten. Кроме того, с момента написания статьи произошли изменения: Autodesk сообщила о прекращении разработки и продажи Stingray.
Зачем это вам
Приход WebAssembly означает, что вам больше не придётся использовать JavaScript для веба, только потому что это единственное, что выполняется в браузере. JavaScript имеет плохую репутацию, хотя на самом деле это хороший язык в том, для чего он предназначен: позволяет быстро писать небольшие скрипты. Однако в настоящее время вы вынуждены использовать его для всего, что запускается в вебе, и это проблема для многих крупных проектов.
Конечно, вы можете использовать лучшие версии JavaScript, такие как TypeScript или даже новые языки вроде Kotlin. Но, в конце концов, все они должны быть скомпилированы в JavaScript. В свою очередь это создало проблемы для разработчиков языка JavaScript, которые должны поддерживать практически все возможные сценарии и все стили программирования. WebAssembly изменит это и позволит всем сосредоточиться на том, что у них получается лучше всего.
Более того, вы можете создать свою собственную реализацию для своих нужд. Вы можете создать оптимизированный компилятор для своего языка. Его можно создать с нуля, а можно добавить поддержку WebAssembly в существующий компилятор. Таким образом, вы можете воспользоваться всеми модулями WebAssembly.
Например, вы можете создать компилятор WebAssembly для DSL, который вы используете внутри вашей компании, и запустить его в сети на стороне клиента без использования настраиваемых плагинов, таких как Oracle Java Plug-in или Adobe Flash.
Как это работает
Основополагающий принцип WebAssembly — хорошая интеграция с существующим миром JavaScript, от технических характеристик, таких как совместимость и общие политики безопасности, до интеграции инструментов, таких как поддержка функции View Source браузеров.
Для достижения этой цели WebAssembly определяет как двоичный формат, так и эквивалентный текстовый формат для инструментов и людей. Технически текстовый формат использует S-выражения, поэтому он будет выглядеть следующим образом:
Однако инструменты, вероятно, покажут нечто более похожее на это (пример из документации):
Если вас интересует, почему в качестве примера используется C++, то это из-за того, что целью начального выпуска (MVP) WebAssembly была поддержка C/C++. Поддержка других языков будет позже; в данный момент они находятся в разработке. Этот выбор был сделан по нескольким техническим и практическим причинам:
Разработчики WebAssembly используют LLVM для сокращения объёма работы, необходимой для получения рабочего продукта. Кроме того, это позволило им легко интегрироваться с другими инструментами, которые работают с LLVM, такими как Emscripten.
Наличие MVP даёт обычным разработчикам возможность тестировать и использовать WebAssebmly, что позволяет соответственно его улучшать.
Инструменты WebAssembly
Первый вариант не очень практичен для общего использования, но он подойдёт, если вы хотите получить представление о формате или начать работу по его интеграции в свои собственные инструменты. В настоящее время есть два набора инструментов: WebAssembly Binary Toolkit и Binaryen.
WABT включает в себя инструменты для разработки и/или использования в инструментах, предназначенных для работы с WebAssembly:
Иными словами, он обеспечивает чистый и лёгкий доступ к данным в формате WebAssembly, чтобы вы могли с ними работать.
С другой стороны, Binaryen — это мощный набор инструментов, предназначенный для использования в инфраструктуре компилятора:
Таким образом, этот набор инструментов готов к интеграции в ваш бэкенд.
По сути, оба позволяют создавать инструменты, которые управляют WebAssembly, но WABT предназначен для инструментов, которые используются во время разработки (например, статический анализ), в то время как Binaryen предназначен для поддержки WebAssembly в компиляторах.
Эти инструменты отлично подходят для тех, кто разрабатывает инструменты и продукты, связанные с компилятором: они предоставляют как инструменты для разработки, так и готовые к использованию в продакшене. Однако они не идеальны для обычных разработчиков, поэтому для них есть более простой способ.
Если вам нужно создать такие инструменты, есть ещё и второй вариант — написать всё с нуля. Этот вариант подойдёт, если вам нужен собственный компилятор или интерпретатор для вашего собственного языка, нужна лучшая производительность или лёгкий инструмент. В этом деле вам может помочь библиотека WasmCompilerKit. Это Kotlin-библиотека, которую вы можете использовать для загрузки WASM-файлов, их изменения и создания. Учитывая то, что она написана на Kotlin, вы можете использовать её также с Java и Scala.
Использование WebAssembly
Если вы просто разработчик, заинтересованный в использовании WebAssembly, то для знакомства с ним вы можете использовать Emscripten (SDK). Emscripten — это набор инструментов, который уже используется для компиляции C/C++ в asm.js. С Emscripten вам будет легче использовать ранее упомянутый Binaryen и интегрировать его со своим собственным набором инструментов.
После установки Emscripten или его компиляции из исходного кода необходимо установить binaryen:
Затем для того, чтобы активировать среду для компиляции и проверить, что установлены правильные пути и переменные, нужно использовать следующие команды:
Наконец, вы можете писать ваш код:
И затем скомпилировать в WebAssembly и увидеть результат в браузере:
Первая команда создаст три файла: модуль WASM, HTML-файл, который показывает код в действии, и JS-файл, который запускает модуль и отвечает за всё, что нужно для его запуска. Параметр WASM=1 говорит Emscripten, что мы хотим сгенерировать модуль WASM вместо asm.js-файла.
Также вы можете напрямую сгенерировать JS-файл, но это не рекомендуется на данный момент, поскольку коду нужно заботиться о таких низкоуровневых вещах, как распределение памяти, утечки памяти и т.д.
Конечная цель состоит в том, чтобы подключать модуль WebAssembly так же легко, как подключается код JavaScript, с помощью HTML-тега
WebAssembly: что и как
Эта статья основана на моём выступлении на конференции ITSubbotnik, прошедшем 2 ноября 2019 года в Москве.
Вообще я бэкенд программист, но меня заинтересовала эта технология, она позволяет использовать мои знания бэкенда на фронте.
Проблема
Начнём с проблемы, которая решается этой (относительно новой) технологией. Проблема эта — быстро исполнять код в браузере. Быстро — это значит, «быстрее чем JavaScript», в идеале настолько быстро, насколько позволяет имеющийся у нас процессор.
Кроме того, исторически, вокруг этой проблемы постепенно возникли важные дополнительные требования:
История до Wasm
Мы видели множество вариантов исполнения кода в браузере. Можно сказать, что на этом поле есть победители и проигравшие.
Победители: это, безусловно, JavaScript; движок V8, сделавший JS таким быстрым; а также HTML5.
Проигравшие: ActiveX — если вы помните, эта технология позволяла делать с машиной вообще всё что угодно, т.е. с безопасностью было очень плохо; Flash — сейчас мы наблюдаем эпоху заката Flash, хотя внутри него работает ActionScript, по сути, тот же JavaScript; Silverlight — наверное, он появился слишком поздно, чтобы занять серьёзную нишу.
В итоге, проиграли все плагины, в том числе из-за проблем с безопасностью.
Были и другие попытки решения проблемы, уже в браузере:
asm.js
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 %).
В целом, asm.js это использование наших знаний о том, как браузерный движок компилирует JS, для того чтобы оптимизировать эту работу.
Что же такое WebAssembly
WebAssembly (или Wasm) — это бинарный формат, запускаемый в браузере, виртуальная машина, и результат компиляции с языка высокого уровня.
Wasm это не язык программирования, подобно тому как байт-код Java это не язык программирования, а результат компиляции и запускаемый блок кода.
Кто-то очень умный сказал, что название web assembly (то есть «ассемблер для веба») полностью неправильное, потому что это не ассемблер (не язык программирования) и он никак не связан с вебом (потому что это просто виртуальная машина).
Создатели WebAssembly руководствовались следующими целями и ограничениями — см. видео Evolving Wasm into a proper misnomer: Andreas Rossberg.
По сути, они сводятся к трём вещам — кросс-платформенность, компактность, скорость. Но было ещё одно важное требование, это «продаваемость» — инициативу должны были воспринять и подхватить разработчики основных браузеров. В итоге это удалось «продать», в разработке спецификации прияли участие представители Google, Mozilla, Microsoft и Apple.
Посмотрим, что представляет из себя Wasm как виртуальная машина.
Это такой «выдуманный процессор», но без регистров, всё делается через стек. Всего четыре типа данных: два целых, два плавающих. Относительно простой набор операций — см. спецификацию и интерактивную таблицу.
Плоская модель памяти: под память выделяется единый блок, размер которого кратен 64 КБ. Здесь находится код, данные, константы, глобальные переменные, стек растущий вниз, куча растущая вверх. Можно сделать так, чтобы куча автоматически увеличивалась при необходимости, при этом блок памяти расширяется на размер кратный 64 КБ.
Указатели не используется (это сделано для безопасности), вместо этого используется индекс. Индекс 32-разрядный, поэтому адресуется до 4 ГБ памяти.
Вся память WebAssembly полностью доступна из JavaScript, причём как на чтение, так и на запись.
Посмотрим на модель исполнения WebAssembly. Wasm всегда загружается и вызывается ТОЛЬКО из JavaScript. Более того, JS и Wasm работают в одной и той же «песочнице», и исполняются одним и тем же движком.
Заметим, что из Wasm также можно вызывать JS. Это может быть вызов функции с передачей аргументов и возвратом значения, либо это может быть просто выполнение произвольной строки как JS-кода.
Пробуем WebAssembly
Для того чтобы освоиться с WebAssembly, я рекомендую воспользоваться сайтом WasmFiddle или WebAssembly Studio — это простой и наглядный способ понять для себя, что такое Wasm на самом деле.
Это текстовое представление с кучей скобочек — по сути, абстрактное синтаксическое дерево (AST). Ну и собственно скобочной записью это напоминает язык Lisp. По идее, мы можем редактировать код в виде текстового представления, и затем свернуть его вновь в бинарный формат — это напоминает программирование на языке ассемблера. Но обычно мы получаем бинарный Wasm в результате компиляции с языка высокого уровня.
2017 год: Production Ready
В ноябре 2017 года WebAssembly был объявлен «готовым к использованию в продакшене». Спецификация на все основные части Wasm была подготовлена, вышла реализация Wasm во всех основных браузерах. Тем самым, для WebAssembly был «выпущен» MVP — Minimum Viable Product, версия 1.0, с которой мы и имеем дело сейчас.
Поддержка в браузерах
В конце 2017 года были выпущены релизы всех основных браузеров с поддержкой WebAssembly:
Исключение — IE11, для него поддержки нет, и по всей видимости, уже не будет. Предполагалось, что для старых браузеров будет polyfill — возможность преобразования Wasm в asm.js; такие прототипы есть, но насколько я видел, эти проекты заброшены, видимо, сообществу не до них.
Сейчас среди всех установленных браузеров,
Поддержка языков
Для того, чтобы ваш любимый язык компилировался в Wasm, нужно чтобы компилятор обеспечивал такую цель компиляции. Сейчас уже довольно много языков поддерживают Wasm, и их становится всё больше с каждым месяцем. См. appcypher/awesome-wasm-langs.
Не так давно появилась новость, что LLVM теперь поддерживает Wasm как цель компиляции. Это облегчает работу для разработчиков языков программирования, мы получим ещё больше языков, компилирующих в Wasm.
Сценарии использования
Наверное, это один из главных вопросов — «А как я могу использовать Wasm?»
Уже сейчас WebAssembly активно применяется:
Другие возможные сценарии:
Производительность
Производительность Wasm была одним из его главных «продающих» факторов, но что с ней происходит на самом деле?
В сравнении с JavaScript, получается, что в среднем Wasm быстрее, но в каждом частном случае нужно делать сравнение JS/Wasm, потому что может получиться и во много раз лучше, и в несколько раз хуже. Также это может сильно зависеть от используемого браузера.
На самом деле, пиковая производительность JS и Wasm одинакова, поскольку оба в итоге превращаются в родной код процессора. Но JS гораздо легче теряет в производительности, а Wasm обеспечивает более «ровный» подход.
Как правило, Wasm хорошо показывает себя на объёмных вычислениях. Там где много операций с памятью, Wasm проигрывает. Ну и основная проблема в реальных применениях — это медленный интероп JS Wasm. См. например, бенчмарк.
В июле 2019 года вышла научная статья «Not So Fast: Analyzing the Performance of WebAssembly vs. Native Code». Авторы реализовали возможность запуска под WebAssembly консольных утилит Linux, для запуска бенчмарков, и использовали бенчмарки SPEC для оценки производительности Wasm по сравнению с теми же тестами на asm.js и на родном коде.
Авторы статьи также дали анализ причин, на чём именно Wasm «подтормаживает»:
В общем, на самом деле, с производительностью всё не так плохо. К тому же, этот анализ позволит разработчикам браузеров сделать Wasm ещё быстрее.
В будущем нас ожидает ускорение Wasm не только за счёт лучшей оптимизации в браузерах, но и за счёт новых фич, таких как: блоковые операции над памятью, поддержка SIMD-инструкций, поддержка threads.
Что будет дальше?
Как развивается WebAssembly?
Во-первых, группа, работающая над спецификациями для Wasm, продолжает свою работу. Спецификации находятся на разных этапах (фазах), есть определённая «дорожная карта» этой работы.
В частности, в ближайшее время мы ожидаем дальше увидеть такие фичи:
Во-вторых, разработчики браузеров, со своей стороны, реализуют эти спецификации, т.е. постепенно к Wasm добавляются новые фичи, сначала скрытые «под флагом» в настройках, а затем и включенные по умолчанию.
Вот, например, список фич Chrome, относящихся к WebAssembly.
Для Firefox подобный список можно найти здесь.
WebAssembly вне браузера
Как было сказано выше, Wasm по сути никак не связан с вебом, это просто виртуальная машина. А значит, его вполне можно использовать и вне веба.
Сейчас видны несколько сценариев использования Wasm вне браузера:
Для Wasm работающего вне браузера, уже не нужны ограничения «песочницы», напротив, необходим доступ к функциям системы — файловая система и файлы, консольный ввод/вывод и т.д. Это привело к созданию WebAssembly System Interface (WASI) — спецификации кросс-платформенного API, подобного POSIX. См. WebAssembly/WASI и wasi.dev.
Заключение
Итак, WebAssembly вполне можно использовать, он уже два года как «production ready».
Применение Wasm вполне может дать некоторое ускорение, по сравнению с аналогичным кодом на JavaScript, но всегда нужно проверять, получился ли прирост скорости.
Поддержка Wasm со стороны языков программирования постоянно развивается.
Ну и самое главное, WebAssembly несколько «изменил ландшафт» веба — предоставил нам новые сценарии использования, которые мы можем реализовать в своих приложениях.
Что это за зверь — WebAssembly?
По мнению многих специалистов WebAssembly представляет будущее эффективных и безопасных вычислений. В чем же конкретно выражаются его достоинства, и почему стоит отнестись к этому языку более внимательно?
Еще в давние времена, когда все развертывали код прямо на «голом железе», наши потребности начали превосходить возможности доступной инфраструктуры, что подтолкнуло ее к эволюционированию в виртуальные машины. В итоге мы получили возможность запускать на одном устройстве до нескольких их экземпляров, заложив, таким образом, основу датацентров.
Когда же наши потребности начали превосходить возможности датацентров, мы вошли в эпоху образов Docker и теперь развертываем эти образы поверх виртуальных машин, развертываемых поверх голого железа.
WebAssembly представляет очередной виток эволюции в системе развертывания. Мы прошли путь от кастомных образов жестких дисков через создание продуманных бандлов для голого железа до виртуальных машин и далее до образов Docker. Теперь же у нас есть еще более компактная, быстрая и при этом портативная единица развертывания – модуль WebAssembly.
Я считаю, что этот инструмент заслуживает внимания, поскольку именно он может стать частью будущего компьютерных вычислений.
Так что же это за зверь — WebAssembly?
WebAssembly – это ни web, ни assembly
При изучении WebAssembly (кратко именуемого Wasm) в сети можно нередко встретить эту фразу. Она открыто заявляет о том, чем Wasm не является, но ничего не говорит о его истинной сути. Надеюсь, что данная статья поможет вам внести в этот вопрос ясность.
Начнем с определения, которое описывает Wasm как виртуальную машину, работающую по принципу стека. Каждая машина (виртуальная или иная) содержит основной набор простых инструкций, с помощью которых она управляется. Независимо от используемого языка программирования в итоге на выходе компилятора получается файл с машинным кодом.
WebAssembly – это низкоуровневый язык. У него есть собственный набор инструкций, но ответственная за их исполнение машина является виртуальной, то есть представляет собой один из процессов системы. Это в глубокой и зачастую недооцененной степени определяет важность WebAssembly.
Используемый в этом языке набор инструкций является портативным. Это означает, что все, выражаемое в байткоде WebAssembly, будет работать не только на исходной системе, но и на любой другой. Виртуальные машины, работающие по принципу стека, такие как Wasm, быстры, потому что код для управления стеками и выполнения над ними операций очень прост и хорошо оптимизируется.
Примитивность
WebAssembly примитивен, но в хорошем смысле. Мне всегда нравились языки, которые делают немногое: языки, чьи разработчики старательно уделили внимание исключению всего лишнего. Значительный потенциал Wasm – о чем я буду говорить в течение статьи – в первую очередь определяется его возможностями.
Единственный нюанс, на который зачастую обращают внимание разработчики (и которого побаиваются) – это присутствие в языке только численного типа данных. Функции могут принимать и возвращать значения в виде целых чисел либо чисел с плавающей запятой, причем допускаются только 32- и 64-битные значения.
Это означает, что такие элементы, как строки, хэш-карты, массивы, деревья, кортежи – все ништяки, воспринимаемые нами как должное – в спецификацию Wasm не входят. Вместо этого предполагается перевод подобных элементов высокоуровневыми языками в байткод Wasm при компиляции.
Этот порядок может не соответствовать спецификации, но в качестве примера сгодится (двоичный формат имеет достаточно сложные требования к кодировке, которые на данном этапе нас только запутают):
Портативность
Я уже упомянул, что никакая инструкция Wasm не ограничивается ни операционной системой, ни архитектурой ЦПУ. Это означает, что при соответствии среды выполнения (например, браузера или кастомного инструмента встраивания (embedder)) спецификации, один и тот же файл Wasm можно интерпретировать на любой машине, независимо от ее ОС или архитектуры ЦПУ.
Быстродействие
Задача среды выполнения проста – считывать коды операций, управлять стеком и памятью с линейной адресацией, а также выполнять диктуемую операционным кодом задачу. Именно эта простота позволяет невероятно быстро обрабатывать файл WebAssembly. Несмотря на доступность только JIT-компиляторов, преобразующих модуль Wasm в нативный код, многие среды выполнения (например, ваш браузер) могут очень быстро работать путем одной только интерпретации.
Потоковость
Интерпретатор может начать выполнение первой инструкции в файле до окончания скачивания остальной его части. При этом ему не нужно беспокоиться об инструкциях перехода, указывающих на еще не скачанные части файла, или о попытках обратиться к еще не раскрытым ресурсам.
В организации файлов Wasm есть некая красота, делающая возможным подобный вид потокового выполнения, которое также поддерживается всеми ведущими браузерами, работающими с WebAssembly.
Компактность
Wasm очень компактен. Даже язык, который обычно производит самые крупные бинарники WebAssembly (речь о Rust), все равно создает их на порядок меньшего размера, чем образы Docker и даже отдельные специфичные для ОС и ЦПУ исполняемые файлы, создаваемые такими языками, как Go и тот же Rust.
Существует ряд фреймворков и кастомных инструментов встраивания, которые за счет скорости и компактности Wasm способны поддерживать сотни и даже тысячи мелких модулей на одном хосте. Подобной вычислительной плотности нереально добиться при помощи доступных на сегодня языков и фреймворков, выполняя компиляцию для «традиционных» целевых платформ.
Безопасность
WebAssembly безопасен. Безопасность в данном случае определяется как возможностями языка, так и его спецификацией. Первая и главная особенность в том, что модуль Wasm реактивен. Он не может делать что-либо, пока не получит соответствующий запрос от среды выполнения. Во-вторых, модули Wasm не имеют доступа к памяти среды выполнения. Они используют собственную закрытую область памяти с линейной адресацией, которая в итоге сводится к длинному вектору байтов.
В WebAssembly нет встроенных инструкций для обращения к файловой системе, записи в сокеты, управления памятью хоста, доступа к сетевым службам или какого-либо взаимодействия с операционной системой. Даже при том, что стандарты вроде WASI позволяют ограниченный доступ к ОС, он оказывается защищен за счет использования разрешающих токенов, и среда выполнения по-прежнему может просто запретить доступ к вызовам функций на основе WASI.
Модули Wasm зачастую представляют собой чистые вычисления либо совсем без побочных эффектов, либо с такими, которые строго контролируются средой выполнения. К побочным эффектам, допускаемым со стороны среды браузера, относятся доступ к JavaScript API через промежуточные оболочки и возможность управлять DOM (также через промежуточный код, обычно генерируемый специальными инструментами).
Подытожим
WebAssembly появился всего несколько лет назад и уже присутствует в каждом браузере, даже если мы о том не знаем. На данный же момент такие компании, как Fastly и CloudFlare, уже экспериментируют с применением Wasm в области граничных вычислений.
Характеристики, которые всегда рассматривались как некий священный Грааль в вычислительной среде – малый размер, портативность, безопасность и быстродействие – все они обеспечиваются WebAssembly. Так что, если вы еще не занялись освоением этой новой технологии, то рекомендую надолго не откладывать, потому что вскоре она наверняка будет использоваться разработчиками повсеместно.