javascript

Как обстоят дела с WebAssembly?

  • понедельник, 9 февраля 2026 г. в 00:00:04
https://habr.com/ru/companies/ruvds/articles/992658/

Когда заходит разговор про WebAssembly, где-нибудь в начале дискуссии обычно появляется комментарий в духе «А что, собственно, произошло?»

Этот язык преподносили как нечто поворотное. Неужели это просто был яркий маркетинг? А может, очередной случай с обречённым на провал апплетом JVM?

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

Содержание

  1. Реальная картина

  2. Что такое WebAssembly

  3. О скорости

  4. Об эффективности

  5. Целевая платформа компиляции

  6. Безопасность и её следствия

  7. Портируемость и встраиваемость

  8. Ещё раз о скорости и размере

  9. Разработка языка и вы

  10. Сноски

Реальная картина

Естественно, WebAssembly активно применяется в реальных сценариях. Вот лишь некоторые примеры его использования:

Для многих из этих инструментов WebAssembly крайне важен как в масштабах всего продукта, так и в качестве одной из основных функций.

Но я думаю, что всё это не особо убедительно. Мы пока не видим, чтобы крупные сайты создавались полностью через фреймворки на базе этого языка. Мы не компилируем свои приложения непосредственно в WebAssembly для обеспечения максимальной портируемости. Но почему?

И для ответа на сей вопрос нужно хорошо понимать, что конкретно представляет из себя WebAssembly. Это позволит нам оценить наиболее эффективные направления его применения, а также возможные ограничения.

Что такое WebAssembly

Если одним словом, то это язык.

О скорости

Сложно отвечать на вопросы в духе «насколько WebAssembly быстрый?». Мы же не спрашиваем о скорости алгебраической нотации, так как понимаем, что это неправильная постановка вопроса.

Если смотреть из контекста того же JavaScript, то скорость языка определяется скоростью выполняющего его движка. Сам JavaScript не имеет скорости, но мы можем оценить быстродействие его движков вроде V8, SpiderMonkey и JavaScriptCore. Ещё можно протестировать производительность его библиотек ввода-вывода, таких как Bun, Deno и Node.

Но под этим вопросом обычно подразумевают «насколько хорошо конструкции языка ложатся на современные аппаратные средства» и «в какой из современных экосистем эти конструкции применяются». 

При грамотном инжиниринге можно любую систему сделать достаточно быстрой ценой некоторых компромиссов. Если вас не пугает компиляция кода напрямую в C, то добиться «околонативной» скорости будет возможно как на JavaScript, так и на WebAssembly.

Всё верно. WebAssembly можно компилировать! Или же интерпретировать его непосредственно — как и в любой системе, всё это будет зависеть от вашей среды выполнения.

Так что давайте теперь поставим вопрос по WebAssembly более предметно: насколько эффективно конструкции этого языка позволяют использовать возможности современного железа? Как оказывается, они в этом весьма неплохи!

Об эффективности

WebAssembly — это довольно близкий аналог языка ассемблера. Хотя различие между ними всё же ощутимо, так как он более высокоуровневый. Тем не менее сходства этих языков достаточно, чтобы компилировать код WebAssembly в большинство видов ассемблера без значительных потерь скорости.

И да, WebAssembly можно писать вручную. Я даже создал специальный курс в стиле rustlings, который назвал watlings. На этом курсе вы можете писать WAT (WebAssembly Text) для выполнения простейших заданий.

WAT очень близок к Wasm. Они почти идентичны тем, что можно сначала скомпилировать WAT в Wasm, а затем обратно, потеряв при этом минимум информации (могут утратиться имена переменных и часть метаданных). Выглядит это так:

(module
  ;; import external i32, name it $global_num_import
  (import "env" "global_num" (global $global_num_import i32))

  ;; A function that adds param $a to $global_num_import, returns i32
  (func $add_to_global_num (param $a i32) (result i32)

    ;; The last stack value is the return value
    (i32.add (local.get $a) (global.get $global_num_import))
  )

   ;; export local function, name it add_to_global
  (export "add_to_global" (func $add_to_global_num))
)

Попробуйте прочесть этот код. Вы найдёте в нём как знакомые, так и чуждые элементы.

Здесь есть функции и S-выражения, а также импорты и экспорты. Но ещё есть инструкции вроде i32.add и неявные возвраты из стека.

Wasm представляет собой байткод и его лучше сравнивать с JVMIS (то есть байткодом JVM). У этих форматов общие цели и ограничения, но они варятся в разных экосистемах и дают разные гарантии.

Wasm отличается значительно меньшим API и более надёжными гарантиями безопасности. Он меньше вмешивается в вашу стратегию управления памятью и сильнее ограничивает спектр действий, которые программа может выполнять без разрешения от рабочей среды.

Он умеет лихо оперировать с цифрами, но для этого ему нужно явно выделить память и предоставить все необходимые импорты. В этом смысле он сильно отличается от реального языка ассемблера (или наиболее популярного).

Чуть позже мы к этому моменту ещё вернёмся.

Целевая платформа компиляции

В Wasm можно компилировать код на многих языках. Среди наиболее ярких Rust, C, Zig, Go, Kotlin, Java и C#.

Даже среды некоторых распространённых интерпретируемых языков вроде Python, PHP и Ruby были перекомпилированы в WebAssembly. Есть и такие, которые компилируются исключительно в Wasm — например, AssemblyScriptGrain и MoonBit.

Для многих из них важно, чтобы не требовался сборщик мусора. Для других, напротив, его наличие окажется желательным. Wasm позволяет реализовать оба этих варианта (хотя опция GC появилась намного позднее).1

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

Как для JVM могут встречаться разные реализации среды выполнения, так и для Wasm есть такие, которые выполняются вне браузера — WasmtimeWasmEdge и Wasmer.

$ Wasmer run cowsay "I am cow"
 __________
< I am cow >
 ----------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
               ||----w |
                ||     ||

Эти языки могут выводить единый артефакт сборки без каких-то конкретных требований к железу вашего ПК. И для его выполнения будет достаточно среды Wasm (обратите внимание на аналогию с JVM).

Безопасность и её следствия

Сейчас Wasm сильно похож на JVM. Основные отличия выражены в механизме управления памятью и количестве платформ, поддерживающих тот или иной формат.

При этом реальным камнем преткновения становится вопрос безопасности.

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

Вы можете легко реализовывать изоляцию в рамках отдельного процесса. И Cloudflare опирается на эту особенность в движке V8, весьма эффективно выполняя недоверенный код за счёт механизма V8 isolates. Это позволяет получить ощутимый прирост эффективности без серьёзных жертв со стороны безопасности.

Если вы избежите запуска отдельного процесса, то программы на Wasm смогут стартовать в 100х быстрее. Компания Fermyon, провайдер услуг по созданию и запуску приложений Wasm, заявляет о времени запуска меньше миллисекунды.

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

Вспомним Flash — мультимедиа платформу, которая использовалась в основном для анимаций и игр, пока в январе 2021 года не была исключена из всех ведущих браузеров. Причиной послужили вопросы к безопасности. Сервис Ruffle возродил поддержку Flash на сайтах вроде Newgrounds, выступив в качестве интерпретатора и VM для выполнения ActionScript.

Cloudflare позволяет выполнять код на Python с аналогичными гарантиями безопасности, что и в случае их JS-кода. Для этого платформа использует интерпретатор Pyodide — по факту CPython, скомпилированный в Wasm.

Figma выполняет недоверенные пользовательские плагины в ваших браузерах, переваривая их с помощью собранного в Wasm движка QuickJS.

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

Портируемость и встраиваемость

Мы разобрали несколько способов запуска программ Wasm. Среда для этого может быть весьма простой. Выбор поддержки Wasm открывает для авторов библиотек широкий спектр вариантов, не принуждая использовать конкретный язык (обычно Lua или JS).

Такие инструменты, как ZellijEnvoy и Lapce предлагают своим пользователям систему создания плагинов на основе Wasm.

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

Сюда относится обработка изображений, ocr, физические движки, механизмы отрисовки, наборы медиа-инструментов, базы данных, парсеры и многое другое.  

Причём в большинстве этих случаев использование Wasm будет весьма прозрачным. Установленная вами библиотека просто будет задействовать его где-то в своём дереве зависимостей.

Кодовые базы Godot и Figma написаны на C++, но зачастую легко подготавливаются для работы в браузере через компиляцию в WebAssembly (или в сочетании с ним).

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

Ещё раз о скорости и размере

Браузеры выполняют Wasm примерно по той же схеме, что и JS. Казалось бы, это должно жёстко ограничивать производительность приложений Wasm. Но зачастую их быстродействие оказывается плюс-минус достойным и зависит от архитектуры или области применения. Такие системы, как WASI позволяют в некоторой степени стандартизировать API, но прослойка всё равно остаётся.

Использование языков с более богатой системой типов и сложными оптимизирующими компиляторами позволяет создавать более эффективные программы. Модель JIT-компиляции в таких движках, как V8 может препятствовать оптимизации, если затраты на неё превышают потенциальную выгоду. Вам будет легче избежать мегаморфных функций, просто отказавшись от JavaScript.

Тем не менее пересечение границ хост-программы несёт свои издержки, особенно при клонировании памяти. На эту тему будет интересно почитать «посмертный» разбор стратегии Zaplib. Постепенный перенос кодовой базы на Wasm ввиду необходимости пересечения границ может повлечь серьёзные затраты, сведя на нет возможность получить какую-либо пользу в краткосрочной перспективе.

Небольшая поверхность API также значит раздутие исполняемого файла, так как системные API чаще воссоздаются, нежели импортируются. Существуют стандарты вроде WASI, призванные помочь в этой ситуации. Но всё же это не нативный строковый тип (пока).

Среди передовых языков минимальные исполняемые файлы Wasm генерирует Zig.2

Фактическое быстродействие Wasm в нативных контекстах (то есть вне движка JS) страдает по ряду причин. Потоковая обработка и ввод-вывод несут определённые издержки. Памяти задействуется больше. Холодный запуск происходит медленнее.

И всё же эти компромиссы производительности могут быть не столь значимы. Уверен, в большинстве случаев код будет «достаточно быстрым». Если же вы работаете в условиях, где быстродействие решает, то преимущества Wasm вряд ли оправдают его потерю.

Разработка языка и вы

Ясное дело, что какие-то процессы происходят.

На YouTube-канале Wasm IO публикуется много интересных выпусков.

По факту между разработкой стандартов и самого языка возникли серьёзные расхождения. Есть сильное желание развития, но стандартизация означает принятие решений, которые будет сложно откатить. Для многих всё движется слишком быстро и не в том направлении.3

Существует «более официальная» рабочая группа W3C и «менее официальная» Bytecode Alliance, которая работает намного быстрее. Она акцентируется на разработке инструментов и языка за рамками непосредственно Wasm (например, создаёт WIT и WebAssembly Component Model).

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

Так почему же люди думают, что ничего не произошло?

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

При этом многие верят в возможность того, что Wasm заменит JS в браузерах — что им вообще не потребуется включать файл .js. Хотя это очень вряд ли.4

Как бы то ни было, вы можете использовать фреймворки вроде Blazor и Leptos, даже не осознавая, что внутренне создаёте артефакты JS.

Чаще всего инструменты Wasm адаптировались и использовались авторами библиотек, а не разработчиками приложений. Так что их внутренние детали остаются туманными. Но это, пожалуй, нормально.

Напоследок скажу, что сообществу, на мой взгляд, никак не помогает философия намеренного усложнения обучающих материалов по Wasm. Это битва, которую я проигрывал неоднократно.5

Ну а пока приглашаю вас попробовать watlings. В будущем я этот проект определённо расширю.

Сноски

  • WasmGC появился в Chrome 119 и Firefox 120 (конец 2023), а также в Safari 18.2 (декабрь 2024 года). До этого некоторые проекты встраивали в исполняемый файл собственный GC. 

  • Zig оказывается максимально близок к С в штатном режиме и обходит его, если применить агрессивную оптимизацию (равномерно в обоих языках). Среди C, Zig, Tinygo и Rust худшие базовые показатели демонстрирует Rust, хотя после серьёзной оптимизации он обходит Tinygo (но не C). При этом размер создаваемого им исполняемого файла оказывается в 3 раза больше, чем размер аналогичного файла, полученного с помощью Zig. Вот примеры кода

  • Читайте этот комментарий

  • Системы вроде WASI позволяют несколько стандартизировать API, но промежуточный слой остаётся. На сегодня ни одна компания-разработчик браузеров не планирует менять что-либо в этом направлении, и преимущества от продвижения в нём не ясны. 

  • Читайте этот ответ