что такое web assembly
Основы WebAssembly
В этой статье объясняются концепции, лежащие в основе работы технологии WebAssembly, включая её цели, проблемы, которые она решает, и то, как она работает в движке рендеринга веб-браузера.
Что такое WebAssembly?
WebAssembly позволяет запускать код, написанный на различных языках в web-приложениях почти c естественной скоростью. Это имеет огромное значение для веб-платформы, так как ранее это нельзя было сделать.
Более того, вам даже не нужно знать, как создавать код WebAssembly, чтобы его использовать. Модули WebAssembly можно импортировать в веб-приложение (или Node.js), и экспортировать из них функции для использования через JavaScript. JavaScript-фреймворки могут использовать модули WebAssembly для получения огромных преимуществ в производительности и новых функций, в то же время делая их функциональность легко доступной для веб-разработчиков.
Цели WebAssembly
Технология WebAssembly создаётся как открытый стандарт внутри W3C WebAssembly Community Group со следующими целями:
Примечание: Технология WebAssembly также будет иметь возможность использования за пределами веб и JavaScript-сред (см. Встраивание вне Web).
Как WebAssembly встраивается в веб-платформу?
Веб-платформа может рассматриваться как состоящая из двух частей:
Исторически ВМ могла загружать только JavaScript. Раньше нас это вполне устраивало, поскольку JavaScript достаточно мощный, чтобы решать большинство проблем, с которыми мы сталкивались в интернете. Однако мы столкнулись с проблемами производительности, когда попытались использовать JavaScript для более нагруженных сценариев использования, таких как 3D-игры, виртуальная и дополненная реальность, компьютерное зрение, редактирование изображений / видео и в ряде других применений, которые требуют повышенной производительности (см. варианты использования WebAssembly где описано больше идей).
Кроме того, длительность загрузки, анализа и компиляции очень больших приложений JavaScript может быть непомерно высокой. Мобильные и другие ограниченные в ресурсах платформы могут ещё более понизить производительность.
Язык WebAssembly отличается от языка JavaScript, но он не предназначен для его замены. Он предназначен для дополнения и работы вместе с JavaScript, что позволяет веб-разработчикам использовать преимущества обоих языков:
Ключевые понятия WebAssembly
Есть несколько ключевых понятий, необходимых для понимания того, как WebAssembly работает в браузере. Все эти понятия отражены 1:1 в WebAssembly JavaScript API.
JavaScript API предоставляет разработчикам возможность создавать модули, объекты памяти, таблицы и экземпляры модулей. Получив экземпляр модуля WebAssembly, код JavaScript может синхронно вызывать его экспорты, которые представляются как обычные функции JavaScript. Любые функции JavaScript также могут синхронно вызываться кодом WebAssembly путём передачи этих функций в качестве импорта в экземпляр модуля WebAssembly.
Поскольку JavaScript полностью контролирует загрузку, компиляцию и запуск кода WebAssembly, разработчики JavaScript могут рассматривать технологию WebAssembly как расширение JavaScript для эффективной генерации высокопроизводительных функций.
В будущем модули WebAssembly будут загружаться так же, как и модули ES2015 (с использованием
Как работает JS: особенности и сфера применения WebAssembly
Сегодня мы представляем вам шестую часть серии материалов, которые посвящены особенностям работы всего того, что связано с JavaScript. Здесь мы поговорим о WebAssembly. А именно, детально проанализируем эту технологию, рассмотрим особенности её работы, а так же то, как она соотносится с обычным JavaScript в плане производительности. Речь пойдёт о времени загрузки кода, о скорости выполнения программ, о сборке мусора, об использовании памяти, о доступе к API платформы, об отладке, о многопоточности и о переносимости WebAssembly-кода. Эта технология, хотя и находится сейчас в самом начале своего развития, уже начала менять взгляды на разработку веб-приложений. Если разработчику нужна высочайшая производительность браузерного кода, ему просто необходимо познакомиться с WebAssembly.
Что такое WebAssembly
WebAssembly (сокращённо — wasm) — это эффективный низкоуровневый байт-код для веб-приложений. Wasm даёт возможность разработки функционала веб-страниц на языках, отличных от JavaScript (например, это C, C++, Rust и другие). Код на этих языках компилируется (статически) в WebAssembly. В результате получается веб-приложение, которое быстро загружается и отличается очень высокой производительностью.
Время загрузки
Wasm — это низкоуровневый язык, похожий на ассемблер. WebAssembly-программы загружаются браузером быстрее, так как через интернет нужно передать уже скомпилированные файлы в весьма компактном бинарном формате.
Выполнение
Сегодня wasm-программы выполняются лишь на 20% медленнее чем машинный код. Это, без сомнения, достойный результат. Ведь речь идёт о формате, который компилируется в особом окружении и запускается с применением множества ограничений, которые обеспечивают высокий уровень безопасности. Подобное замедление в сравнении с машинным кодом в этом свете выглядит не таким уж и большим. Кроме того, в будущем ожидается повышение производительности wasm-кода. Ещё интереснее то, что wasm платформенно-независим. Его поддержка имеется во всех ведущих браузерных движках, которые демонстрируют примерно одинаковую производительность при выполнении wasm-кода.
Для того, чтобы сопоставить особенности wasm и JavaScript, вы можете обратиться к этому материалу, в котором идёт речь об особенностях JS-движков на примере V8. А сейчас мы поговорим о том, как wasm-код соотносится с другими механизмами V8.
Wasm и JS-движок V8
Вот схема устройства движка V8, а именно, тот путь, который проходит программа на JavaScript от простого текстового файла до исполняемого кода.
Динамическая компиляция в V8
Слева представлен исходный код на JavaScript, который содержит некую функцию. Сначала этот код подвергается парсингу, строки превращаются в токены и генерируется абстрактное синтаксическое дерево (Abstract Syntax Tree, AST). AST — это представление логики JS-программы. После создания AST V8 преобразует то, что получилось, в машинный код. Производится обход абстрактного синтаксического дерева и то, что раньше было функцией, существующей в виде текста, преобразуется в её скомпилированный вариант. При этом V8 не прилагает особых усилий для того, чтобы оптимизировать код.
Посмотрим теперь на то, что происходит на следующих стадиях работы движка.
Конвейер движка V8
Здесь показано, что теперь приходит время TurboFan — одного из оптимизирующих компиляторов V8. Во время работы JavaScript-приложения в V8 производится множество вспомогательных действий. А именно, TurboFan наблюдает за происходящим в поисках узких мест и наиболее часто используемых фрагментов программы для того, чтобы оптимизировать соответствующие участки кода. После этого TurboFan обработает наиболее ответственные части программы, пропустив их через оптимизирующий JIT-компилятор, что приведёт к тому, что те функции, которые «съедают» большую часть времени процессора, станут выполняться гораздо быстрее.
Этот подход позволяет повысить производительность выполнения JavaScript, решает проблему скорости работы программ, но и тут не всё гладко. Дело в том, что операции по анализу кода и по принятию решений о том, что нужно оптимизировать, а что не нужно, так же потребляют ресурсы. Это, в свою очередь, означает более высокое энергопотребление систем, что, в случае с мобильными устройствами, ведёт к сокращению срока их жизни от одной зарядки.
Если же включить в вышеприведённую схему wasm, то окажется, что этот код не нуждается в анализе и в нескольких проходах компиляции. Он уже оптимизирован и готов к использованию.
Wasm и конвейер V8
Wasm-код оптимизируется в ходе статической компиляции. При работе с ним не нужно разбирать текстовые файлы. Благодаря wasm в нашем распоряжении оказываются бинарные файлы, которые достаточно лишь преобразовать в машинный код. Все улучшения в этот код были внесены при компиляции, которая производится до того, как он попадает в браузер.
Всё это делает выполнение wasm гораздо более эффективным, так как немало шагов по превращению текста программы в оптимизированный машинный код можно пропустить.
Модель памяти
Доверенная и недоверенная области памяти WebAssembly
Память программы, написанной, например, на C++ и скомпилированной в WebAssembly, представляет собой непрерывный блок, в котором нет «дыр». Одна из особенностей wasm, которая способствует повышению безопасности, заключается в отделении стека выполнения от линейного адресного пространства. В C++-программах есть куча, память под стек выделяется в её верхней части. При подобной организации работы с памятью можно, воспользовавшись указателем, получить доступ к памяти стека для того, чтобы воздействовать на состояние переменных, взаимодействие с которыми на текущем этапе выполнения программы не предусмотрено. Именно эту возможность используют многие вредоносные программы.
WebAssembly пользуется совершенно другой моделью работы с памятью. Стек выполнения отделён от памяти, где хранится сама wasm-программа, в результате нет возможности получить несанкционированный доступ к этой памяти, и, например, изменить состояние каких-то переменных. Кроме того, функции используют не указатели, а целочисленные смещения. Здесь применяется механизм косвенной адресации. Необходимые прямые адреса вычисляются в процессе работы программы. Этот механизм построен так, что можно одновременно загрузить несколько wasm-модулей, адреса будут находиться с использованием смещений, в итоге всё будет работать как надо.
Подробнее о механизмах управления памятью в JavaScript можно почитать здесь.
Сборка мусора
В предыдущих материалах этой серии мы уже говорили о том, что в управлении памятью JS-программ участвует сборщик мусора (Garbage Collector, GC).
В случае с WebAssembly всё выглядит немного иначе. Эта технология поддерживают языки с ручным управлением памятью. В результате вместе с wasm-модулями можно использовать и собственный сборщик мусора, но это — непростая задача.
Кроме того, эти языки не созданы для выполнения сложных операций, которые обычно реализуются с помощью JavaScript, таких, как манипуляции с DOM. Нет смысла писать HTML-приложение целиком на C++, так как C++ просто не рассчитан на такое применение. В большинстве случаев код для веб-приложений на C++ или Rust пишут для работы с WebGL или создают на нём высокооптимизированные библиотеки, например, такие, которые отвечают за математические вычисления.
В будущем, однако, ожидается поддержка языков, которые используют другие модели работы с памятью.
Доступ к внешним API
В зависимости от среды выполнения, JavaScript программа может напрямую взаимодействовать со специализированными API. Например, если программа написана для браузера, в её распоряжении оказывается набор Web API, которые приложение использует для управления браузером или устройством, и для работы с DOM, CSSOM, WebGL, IndexedDB, Web Audio API и так далее.
Нельзя говорить, что так будет всегда. В будущем ожидается появление соответствующих API для непосредственного использования их в wasm-коде. Как результат, wasm-приложения можно будет создавать, не используя обращения к JavaScript.
Карты кода (source maps)
Если после минификации JS-кода его нужно отлаживать, в дело вступают карты кода (source maps). Это — способ установления соответствий между JS-кодом, который минифицирован или скомбинирован из различных файлов, и его исходным состоянием. Когда проект собирают для продакшна, производят минификацию и комбинирование файлов, создают и карту кода, которая хранит информацию об исходных файлах. При обращении к конкретному месту в сгенерированном коде можно, сверившись с картой кода, найти фрагмент исходной программы, который выглядит гораздо понятнее, нежели упакованный вариант программы.
WebAssembly не поддерживает карты кода (source maps), так как пока нет соответствующей спецификации, но, вполне возможно, что уже весьма скоро такая возможность появится.
В результате, при отладке wasm-кода, полученного, например, из C++, можно будет видеть исходный код, устанавливать в нём точки останова. По крайней мере, такова цель внедрения карт кода в wasm.
Многопоточность
JavaScript выполняется в одном потоке, хотя он поддерживает асинхронную модель программирования. Подробнее об этом можно почитать здесь. Кроме того, JS поддерживает технологию Web Workers, но у неё довольно специфическая область применения.
Преимущественно — вычисления, интенсивно использующие ресурсы процессора, которые могут заблокировать главный поток пользовательского интерфейса. Однако, код Web Workers не может работать с DOM.
В настоящий момент WebAssembly не поддерживает многопоточность. Однако, вероятнее всего, эта возможность появится совсем скоро. Wasm будет близок к низкоуровневым потокам (то есть — тем, которые используются в C++). Возможность работать с «настоящими» потоками создаст множество новых возможностей в разработке браузерных приложений. Но, конечно, многопоточность означает и появление новых сложностей.
Переносимость кода
JavaScript-приложения могут работать практически везде: в браузерах, на серверах, даже во встраиваемых системах.
WebAssembly спроектирован с учётом безопасности и возможности переносимости кода. Этим он очень похож на JavaScript. Он будет работать в любом окружении, поддерживающем wasm, то есть, например, в любом браузере.
В плане переносимости перед WebAssembly стоят те же цели, которых в своё время пытался достичь Java посредством апплетов.
Итоги
В ранних версиях WebAssembly особое внимание уделялось тяжёлым вычислениям, например, математическим. Очевидная область применения таких вычислений — игры, в которых приходится работать с мириадами пикселей. Подобные приложения можно писать на C++/Rust с использованием привычных методов работы с OpenGL и компилировать то, что получится, в wasm. После чего всё это будет работать в браузере. В качестве примера откройте эту ссылку в Firefox. Тут применяется Unreal Engine.
Ещё один вариант использования WebAssembly, ориентированный на повышение производительности веб-приложений, заключается в реализации с использованием этой технологии библиотек, выполняющих ресурсоёмкие вычисления, например — это библиотеки для обработки изображений.
Использование wasm может довольно серьёзно снизить потребление энергии аккумуляторов на мобильных устройствах (конечно, зависит это и от движка), так как большинство вспомогательных операций по подготовке программы к выполнению будет выполнено в ходе статической компиляции кода.
Ожидается, что в будущем у нас появится возможность использовать бинарные wasm-файлы, даже не занимаясь написанием кода, который в них компилируется. В NPM можно найти проекты, которые реализуют такой подход.
Можно ли говорить о том, что wasm заменит JS? На данном этапе развития технология — определённо нельзя. Например, если речь идёт о работе с DOM или об использовании API платформы, на которой выполняется код, у JavaScript пока нет альтернативы, так как эти API доступны из JS-программ напрямую, без добавления дополнительных уровней абстракции.
Автор материала отмечает, что в SessionStack с интересом наблюдают за WebAssembly, рассчитывая ускорить с помощью этой технологии наиболее ресурсоёмкие части своих разработок.
Предыдущие части цикла статей:
Уважаемые читатели! Планируете ли вы пользоваться WebAssembly в своих проектах?
Знакомство с WebAssembly
Эта статья основана на моём выступлении на ITSubbotnik, прошедшем в Рязани 14 октября 2017 года. На русском пока что довольно мало материала на эту тему, надеюсь что статья будет вам полезна.
Disclaimer: Автор не является экспертом ни в WebAssembly, ни в JavaScript. Данная статья есть компиляция мыслей и идей, полученных из выступлений других людей на данную тему, плюс эпизодического опыта изучения WebAssembly в течение нескольких месяцев.
Что такое WebAssembly?
WebAssembly (WASM) — новый бинарный формат, позволяющий запускать код в браузере.
Пока нам будет достаточно такого определения, более полное определение можно найти в Википедии.
Проблема
Давайте разберёмся сначала, какую проблему или задачу пытались решить, создавая WebAssembly. Проблема далеко не новая, фактически это — быстро исполнять код в браузере. Но не всё так просто, постепенно так сложилось, что помимо самой проблемы, у нас есть ещё несколько сопутствующих требований:
Ситуация
В попытке решения этой проблемы у нас есть один победитель, и это JavaScript.
Проигравшие (далеко не полный список):
Другие попытки решения:
В целом, все попытки решения можно разбить на две группы:
Решение 1: Родной код прямо в браузере
Примеры: ActiveX, NaCl
Что плохо: нет портируемости, потенциальные или реальные проблемы с безопасностью.
Решение 2: Код для виртуальной машины
Примеры: Java Applets, Silverlight и др.
Что плохо: нужен плагин и/или runtime ⇒ нет zero configuration
В целом, если вы хотите обеспечить кросс-платформенное выполнение вашего кода, то виртуальная машина это правильный подход.
Что не так с JavaScript?
С JavaScript всё хорошо. Но если смотреть на рост его производительности с годами, то мы увидим что он сейчас находится на втором «плато» S-образной кривой. Сначала производительность была небольшой и росла постепенно, с появлением V8 мы увидели резкий скачок, который уже достаточно давно вновь перешёл в плавный рост.
(Картинка из статьи An Abridged Cartoon Introduction To WebAssembly by Lin Clark.)
Посмотрим как работают современные движки JavaScript.
Прежде всего, исходный код (текст на JS) проходит через парсер, в результате возникает внутреннее представление кода — абстрактное синтаксическое дерево. Дальше работает интерпретатор. Отдельные функции при исполнении преобразуются в байт-код — по сути, последовательность вызовов внутренних функций интерпретатора. При этом накапливается статистика использования JS-функций. Если для какой-то отдельной функции преоделён порог вызовов, то принимается решение о том, что её нужно оптимизировать и она передаётся компилятору. Компилятор генерирует машинный код, который сильно завязан на типы входных значений.
Допустим, у нас есть функция с двумя аргументами: foo(a, b), и мы вызываем её много раз с числовыми значениями параметров. В некоторый момент функция будет передана компилятору и станет выполняться быстрее. Допустим, мы вызовем её со строковым аргументом. В результате, движок выполнит «деоптимизацию»: передаст функцию от компилятора обратно интерпретатору, а готовый машинный код будет выброшен.
Что я хочу этим сказать? Разработчики JavaScript движков делают отличную работу и спасибо им за это. JavaScript вовсе не плох, но у него есть внутренние ограничения, которые уже не позволят сделать его радикально быстрее.
asm.js
Ещё одна интересная инициатива, уже от Mozilla Foundation, которая подводит нас вплотную к теме WebAssembly. Появилась она в 2010 году, а в 2013 стала публично доступна.
Идея заключается в том, что создано подмножество JavaScript, в которое можно компилировать код из C и C++ с помощью специального компилятора Emscripten.
Поскольку это подмножество JavaScript, то такой код будет исполняться в любом браузере. Кроме того, основные современные браузеры уже давно умеют быстро распознавать asm.js и эффективно компилировать его в родной код процессора. В сравнении с родным кодом, полученным непосредственно из C/C++, код полученный из asm.js код медленнее всего в 1,5-2 раза (50-67 %).
Для простейшей функции на C/C++ код asm.js выглядит так:
Здесь ‘use asm’ это директива, показывающая JS-движку что код ниже это asm.js, и конструкции вида |0 показывают, что работа идёт с целыми числами (побитовое ИЛИ с нулевым значением обнуляет дробную часть Number).
Цели разработки WebAssembly
Так что же такое WebAssembly?
С чего начать? Hello World
Освоение WebAssembly я очень советую начать с онлайнового инструмента WasmFiddle.
(Сам я начал с Emscripten и понял свою ошибку лишь спустя время.)
Слева вверху исходный код, слева внизу результат компиляции по кнопке Build (сейчас видно текстовое представление), справа вверху код для запуска и справа внизу результат запуска по кнопке Run.
В качестве примера я использовал простой код для расчёта числа Фибоначчи (да, опять он :), не сказать чтобы хороший код, просто первый попавшийся нагугленый вариант:
Немного про текстовое представление (WAST). Как уже говорилось, WebAssembly это бинарный формат, на выходе компиляции мы получаем WASM-файл. Текстовое представление всегда можно получить из WASM-файла, оно позволяет разобраться в том, что именно содержит сборка, какие таблицы и код. Также это представление используется для отладки.
В данном случае, по текстовому представлению мы видим, что выделяется 1 страница памяти (каждая страница = 64 Кбайт), наружу видны (экспортируются) память и функция fib, а дальше идёт определение этой функции, то есть собственно её реализация.
Начало текстового представления этой сборки выглядит так:
Если собрать всё вместе, то минимальный JavaScript-код для запуска примера выглядит примерно так:
Здесь готовый WASM описывается в коде в виде массива чисел, но в реальной жизни конечно WASM-файл будет довольно намного больше, и мы будем подгружать его с сервера.
Исполнение WebAssembly в браузере выглядит так. Браузер как обычно загружает HTML-страницу, с которой выполняется JavaScript, который уже выполняет загрузку WebAssembly — получается «модуль» (WebAssembly module), затем создаёт экземпляр модуля, после чего можно вызывать для этого экземпляра экспортируемые им функции.
Обратите внимание здесь на серую стрелку: изнутри WebAssembly можно вызывать функции JavaScript. Рассмотрим это подробнее на диаграмме последовательности:
Здесь мы сначала из JavaScript вызываем WebAssembly, затем из WebAssembly вызываем функцию JavaScript.
Во втором вызове здесь я показал то, как WebAssembly пользуется любыми API (например, DOM / WebGL итд.). Это возможно, но не напрямую, такие вызовы тоже происходят только через JavaScript. Очевидно, здесь возникает «бутылочное горлышко»: если мы будем интенсивно работать с API из WASM, то будем терять много времени на «прокидывании» этих вызовов через JavaScript.
Модель памяти WebAssembly очень проста. Это плоский «кусок» памяти, в котором находится код программы, глобальные переменные, стек и куча. Есть возможность сделать так, чтобы память была расширяемой, то если если при очередном выделении памяти нам не хватает места, то верхняя граница памяти автоматически увеличивается.
Весь блок памяти доступен из JavaScript, просто как массив байтов (и кроме того, как массив 16- и 32-разрядных слов, как массив 16- и 32-разрядных float-значений). Причём, память из JavaScript доступна как на чтение так и на запись.
Emscripten
Emscripten это основной компилятор для получения asm.js и WebAssembly из C/C++. (Существуют также компиляторы в WASM из других языков, например из Rust и TypeScript.) Здесь я буду рассматривать использование Emscripten под Windows, но не думаю что для других систем будут существенные различия.
Говоря об Emscripten, стоит немного рассказать о Low Level Virtual Machine (LLVM).
LLVM это семейство компиляторов. Основная идея LLVM это разделение компиляции на frontend и backend. Frontend-компилятор занимается компиляцией из исходного кода во внутреннее представление (Intermediate Representation, IR). IR это код для некоторой виртуальной машины. Backend-компилятор занимается уже преобразованием IR в код для конкретной платформы, например, часто используется backend для x86 и x86-64. Если нужен компилятор с другого языка программирования, то пишется только новый frontend. Если нужна компиляция под новую платформу, то пишется новый backend.
Emscripten задействует LLVM для компиляции из C/C++, и предоставляет свои backend-компиляторы для сборки в asm.js и WebAssembly.
Установка Emscripten
Установка Emscripten проходит довольно просто, в моём случае это было под Windows, и мне даже не понадобилось ничего компилировать из исходников.
Компиляция в asm.js
Посмотрим как можно собрать код с помощью Emscripten, начнём с asm.js.
Пример тот же что и выше, немного модифицированный для Emscripten (fib.c):
Для компиляции я использую следующий командный файл:
На выходе компиляции мы получаем HTML-файл (fib.html), включающий JavaScript для запуска скомпилированного кода:
Также мы получили файл fib.js, в недрах которого можно найти функцию fib() и её вызов в main():
Кроме того, генерируется двоичный файл fib.html.mem — это «образ памяти», то как выглядит память перед запуском программы, здесь все константы и глобальные переменные.
Открыв fib.html мы увидим вот такую картину:
Компиляция в WebAssembly
Для компиляции в WebAssembly нам вообще не нужно менять исходник на C/C++ (и это прекрасно!).
Мы немного изменяем только командную строку вызова компилятора:
В результате компиляции мы также получаем fib.html, плюс fib.js (набор сервисных функций Emscripten), плюс наконец-то fib.wasm:
В начале WASM-файла стоит байт 00 и затем символы «asm», по этим первым четырём байтам можно определить, что мы загружаем именно wasm, а не какую-нибудь страницу-заглушку с кодом ошибки. Следующие 4 байта это номер версии, в данном случае 1.0. Отдельного файла для образа памяти не генерируется, константы и глобальные переменные включены в тот же WASM-файл.
Скриншот с результатом я здесь приводить не буду, он выглядит один-в-один точно так же как для примера с asm.js.
Давайте посмотрим что у нас есть в плане отладки. Открыв инструменты разработчика Chrome (F12), переходим в Sources и там увидим новый раздел «wasm», в котором среди набора блоков можем найти нашу функцию, можем поставить там точку останова, остановиться на ней и пошагать в отладчике.
Как видите, для отладки используется текстовое представление (WAST), о котором я говорил выше.
Посмотрим что нам это даст в плане отладки. Обновив страницу, в разделе Sources мы теперь увидим наш исходник fib.c, можем поставить в нём точку останова, остановиться на ней, просмотреть локальные переменные и пошагать отладчиком по коду.
Возможности Emscripten
Emscripten развивается с 2010 года и уже много чего умеет. В данном случае речь уже не о компиляторе, а о поддержке популярных библиотек, используемых из кода на C/C++. Поддерживаются:
И другие возможности:
Более сложный пример
У меня есть свой хобби-проект эмулятора старого советского компьютера, написанный на C++, я его описывал вот в этой статье. С тех пор я успел его несколько дописать, а также портировать под Qt (Windows/MacOS/Linux). Так что у меня уже было выделено ядро эмуляции (
7 килострок), которое собиралось под разными компиляторами. Собственно я и начал изучение WebAssembly с того, что компилировал этот эмулятор с помощью Emscripten. До первого удачного запуска у меня ушло два вечера после работы, я считаю это неплохой результат, говорящий о том, что порог вхождения в тему относительно низкий. Большая часть работы была связана с JavaScript: как из JS вызывать методы WASM, как передавать параметры, как отрисовывать canvas итп.
Кстати, экран эмулируемой машины формируется полностью внутри WASM, в виде сплошного блока памяти, с форматом «пикселей», подходящем для canvas. Наружу в JavaScript передаётся только адрес готового «экрана». В JavaScript остаётся только скопировать этот блок памяти в canvas.
Итак, подводя итоги по Emscripten. Я убедился в том, что из одного и того же кода он генерирует результат в виде asm.js и в виде WebAssembly, полученный результат выглядит и работает в точности одинаково (кроме скорости конечно). Порог вхождения до получения реального результата оказался относительно низким.
С другой стороны, Emscripten довольно сложный и «нафаршированный» инструмент. В результат компиляции он включает массу вещей которых вы не ожидаете, но которые могут вам пригодится. Поэтому даже для небольших по объёму исходников генерируется большой объём результирующего кода. Часть таких вещей можно отключить опциями командной строки, часть нет.
Думаю что не стоит начинать освоение WebAssembly сразу с Emscripten, по той причине что довольно сложно для себя отделить то что даёт Emscripten от того что имеет WASM «из коробки». Но на реальных проектах Emscripten более чем полезен, именно за счёт тех возможностей, которые он предоставляет разработчику.
Текущее состояние WebAssembly
Новости WebAssembly в 2017 году:
Поддержка в браузерах
На начало октября 2017 года ситуация выглядела примерно так:
Версия Edge привязана к версии операционной системы. Вместе с Windows 10 Fall Creators Update мы получаем Edge 16, в котором WebAssembly работает сразу, уже не нужно ничего включать в настройках.
Для браузеров, не поддерживающих WebAssembly, предполагается использовать т. н. «polyfill», то есть автоматическое преобразование WASM в код, который может выполняться в данном браузере. В частности, был сделан прототип, эффективно выполняющий преобразование WebAssembly в asm.js. Но пока я не видел примеров реального применения такого подхода.
Будущее WebAssembly
Ряд вещей над которыми команда WebAssembly работает сейчас:
Производительность
Вопрос по производительности на самом деле довольно сложный. Потому что не всегда WebAssembly работает быстрее того же JavaScript или asm.js. Например, посмотрите сравнения из JavaScript vs WebAssembly easy benchmark. На первом же тесте collisionDetection у меня получается что WASM даёт 88% от JS. А допустим на следующем тесте Fibonacci WASM выдаёт гораздо лучший результат, в 3 раза быстрее чем JS.
Отмечу здесь только несколько моментов, влияющих на скорость, реально их конечно гораздо больше.
Выше уже отмечал, что WebAssembly может значительно терять в производительности при интенсивных вызовах JS-функций.
WebAssembly теряет в производительности на обращениях к памяти: каждое такое обращение делает проверку на выход за границы доступного блока памяти.
WebAssembly может существенно выигрывать за счёт типа целых переменных. В JS у нас есть только тип Number, фактически всегда 64-разрядный с плавающей точкой, и целые числа это плавающее число без дробной части. При компиляции в JS-движке, для целых чисел используется 64-разрядный целый тип. В WASM мы сами выбираем разрядность типа, и если мы используем 32-разрядный целый тип, операции над которым немного быстрее чем над 64-разрядным целым, то мы получаем здесь «нечестное» преимущество в скорости вычислений.
В целом, у меня сложилось мнение, что нет такого что «в среднем WebAssembly обеспечивает 10-15% прирост скорости», нет никакого «в среднем», для каждого алгоритма нужно тестом определять, получите ли вы прирост скорости, применив WASM. Но в целом, можно предсказать что для интенсивных вычислений, скорее всего, WebAssembly даст какой-то более или менее ощутимый прирост производительности. Ну и кроме того, видно что уже даже за последние полгода скорость WASM несколько выросла с выходом новых версий браузеров.
Заключение
Применение WebAssembly
Варианты использования
Ссылки
Материала по теме очень много, но он практически весь на английском. Здесь я собрал несколько ссылок которые считаю наиболее полезными.