Реклама
Перетяжка // Коробка 3.0

Wasmi 2.0 ускорил интерпретацию WebAssembly в 2,2 раза

Геометрическое среднее по бенчмаркам авторов: примерно в 2,2 раза быстрее Wasmi 1.0.

Обложка: Wasmi 2.0 ускорил интерпретацию WebAssembly в 2,2 раза

Робин Фрайлер, автор интерпретатора WebAssembly Wasmi, 1 сентября объявил о выходе Wasmi 2.0. По замерам самого проекта, новая версия исполняет Wasm-код примерно в 2,2 раза быстрее Wasmi 1.0 по геометрическому среднему набора бенчмарков. Для тех, кто запускает Wasm там, где JIT недоступен или нежелателен, это означает заметное ускорение без смены рантайма.

Wasmi написан на Rust и рассчитан на сценарии, где нужен именно интерпретатор: плагины в приложениях, встраиваемые устройства, песочницы, смарт-контракты. Релиз опубликован на GitHub и crates.io; работа над ним заняла восемь месяцев.

Ключевые выводы
  • Wasmi 2.0 примерно в 2,2 раза быстрее Wasmi 1.0 по геометрическому среднему; замеры вендора на Apple M2 Pro, AMD EPYC 7763 и Intel Xeon Platinum 8370C.
  • Появились четыре режима dispatch; indirect-threaded на 10–15% медленнее direct-threaded, но экономит память под IR.
  • Новое промежуточное представление с аккумуляторными регистрами ireg, freg32 и freg64 и 64-битными ячейками стека.
  • Fuel metering стал стабильным и привязан к входному Wasm-коду; появился deterministic profile и новый CLI.
  • Включение SIMD больше не замедляет обычный код: в 1.0 это стоило около 8%, в 2.0 разница в пределах шума.

Откуда взялись 2,2 раза

Автор подробно разбирает три источника ускорения. Первый: режимы диспетчеризации инструкций. В 1.0 был один switch-loop, теперь их четыре: direct-threaded, indirect-threaded, switch-loop и call-loop. Режим auto-dispatch включён по умолчанию и выбирает threaded-код там, где компилятор и платформа это позволяют. Indirect-threaded вариант примерно на 10–15% медленнее direct-threaded, зато требует меньше памяти на промежуточное представление, что важно для микроконтроллеров.

Второй источник: новое промежуточное представление. В 1.0 операнды адресовались смещениями в стеке; в 2.0 появились аккумуляторные регистры ireg, freg32 и freg64, через которые проходит большая часть значений. Ячейки стека стали всегда 64-битными, а SIMD-значения занимают две соседние ячейки, поэтому поддержка SIMD больше не стоит производительности обычного кода.

Третий: структура CodeMap, в которой хранятся скомпилированные функции. Она стала append-only с раздельными buckets, поэтому вызов внутренней функции на горячем пути не требует ни одного lookup и не берёт блокировок. Доступ к данным экземпляра модуля, по бенчмарку count/globals, улучшился примерно на 35%; таблицы Wasm уменьшились в 2–4 раза за счёт 32-битных ссылок.

Wasmi 2.0 ускорил интерпретацию WebAssembly в 2,2 раза_7
Геометрическое среднее времени исполнения по набору wasmi-benchmarks на Apple M2 Pro. Источник: Wasmi Labs

Как измеряли и с кем сравнивали

Замеры сделаны на проекте wasmi-benchmarks, который можно запустить самостоятельно, на трёх машинах: Apple M2 Pro, AMD EPYC 7763 и Intel Xeon Platinum 8370C. Кроме Wasmi 1.0 в сравнении участвовали интерпретаторы Wasm3, WAMR в режиме fast-interpreter, Wasmtime Pulley и Stitch. Это замер вендора: набор тестов, машины и конфигурации выбирал сам проект, и автор оговаривает, что цифры зависят от нагрузки.

В посте есть поучительный эпизод про компилятор. Rust 1.92 включил оптимизацию DestinationPropagation, из-за чего CoreMark у интерпретатора Stitch упал с более чем 3000 до примерно 2200 баллов; после исправления результат вернулся выше 3000. Похожая проблема нашлась и в Wasmi: её устранение подняло CoreMark примерно с 2800 до более чем 4200 баллов, то есть примерно на 50%. Вывод для практики: производительность интерпретатора на Rust заметно зависит от версии компилятора, и после обновления toolchain бенчмарки стоит перепрогонять.

Что изменилось кроме скорости

  • Fuel metering, то есть учёт «топлива» на выполнение, стал стабильным и считается по входному Wasm-коду, а не по внутренним инструкциям; это важно для смарт-контрактов и лимитов на плагины.
  • Deterministic profile гарантирует одинаковый результат на разных платформах.
  • Поддержка memory64 стала опциональной; отключение валидации через feature validate уменьшает бинарник примерно на 200–300 КБ.
  • Обновлённый CLI и C-API.

Кому обновление ничего не даст: тем, кто запускает Wasm через JIT-рантаймы вроде Wasmtime или V8 на сервере и в браузере. Интерпретатор нужен там, где JIT запрещён (iOS, некоторые блокчейн-среды), где нет памяти под скомпилированный код или где важна предсказуемость времени запуска.

Как попробовать

Wasmi ставится как обычный crate из crates.io; для Rust-проекта достаточно обновить зависимость wasmi до версии 2.0 и прогнать тесты: автор предупреждает о смене внутреннего представления, а значит, поведение при ошибках и лимитах стоит перепроверить. Проект открыт, спонсируется Stellar Development Foundation с октября 2024 года.

В планах на Wasmi 3.0 названы оставшиеся части WebAssembly 3.0: function-references, exception-handling и gc. Сроков автор не называет.

Источники: Блог Wasmi Labs: Wasmi v2.0, Релиз v2.0.0 на GitHub

Изображение на обложке: Wasmi Labs