Хабрахабр

[Перевод] Как мы при помощи WebAssembly в 20 раз веб-приложение ускорили

image

В этой статье рассматривается кейс по ускорению браузерного приложения через замену вычислений JavaScript на WebAssembly.

WebAssembly — что это такое?

Если коротко, то это это бинарный формат инструкций для стековой виртуальной машины. Часто Wasm (сокращенное название) называют языком программирования, но это не так. Формат инструкций исполняется в браузере наряду с JavaScript.

Здесь применяется статистическая типизация и так называемая плоская модель памяти. Важно, что WebAssembly можно получить при компиляции исходников на таких языках, как C/C++, Rust, Go. Эти возможности и привели к росту популярности WebAssembly. Код, как сказано выше, хранится в компактном бинарном формате, благодаря чему выполняется почти так же быстро, как если бы приложение было запущено с помощью командной строки.

Напоминаем: для всех читателей «Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».

Skillbox рекомендует: Практический курс «Мобильный разработчик PRO».

На данный момент Wasm используется во многих приложениях, от игр вроде Doom 3 до портированных в веб приложений типа Autocad и Figma. Wasm применяется и в такой сфере, как serverless вычисления.

Для наглядности мы взяли работающее приложение, написанное на С, которое скомпилируется в WebAssembly. В этой статье приведен пример использования Wasm для ускорения аналитического веб-сервиса. Результат будет использован для замены малопроизводительных участков JS.

Трансформация приложения

В примере будет использоваться браузерный сервис fastq.bio, который предназначен для генетиков. Инструмент позволяет оценить качество секвенирования (расшифровки) ДНК.

Вот пример приложения в работе:

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

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

На входе — данные, представляемые в виде текстового файла. Работает все просто. В файле размещается список последовательностей ДНК и оценка качества для каждого нуклеотида. Этот файл генерируется специализированными инструментами для секвенирования. Формат файла .fastq, поэтому сервис и получил такое название.

Реализация на JavaScript

Первый шаг пользователя при работе с fastq.bio — выбор соответствующего файла. Используя объект File, приложение считывает случайную выборку данных из файла и обрабатывает этот пакет. Задача JavaScript здесь — выполнение несложных строковых операций и подсчет показателей. Один из них — количество нуклеотидов A, C, G и T на разных фрагментах ДНК.

Разделение на фрагменты сделано для повышения качества UX. После просчета нужных показателей они визуализируются при помощи Plotly.js, а сервис начинает работать с новой выборкой данных. Сервис же берет участки данных размером от 0,5 до 1 Мб и работает с ними шаг за шагом, выстраивая графические данные. Если работать со всеми данными сразу, процесс зависнет на какое-то время, поскольку файлы с результатами секвенирования занимают сотни гигабайтов файлового пространства.

Вот как это работает:

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

Тестируем WebAssembly

Для оценки возможности использования Wasm команда проекта занялась поиском готовых решений для создания QC-метрики (QC — quality control) на основе файлов fastq. Поиск велся среди инструментов, написанных на С, С++ или Rust, чтобы была возможность портировать код на WebAssembly. Кроме того, инструмент не должен быть «сырым», требовался сервис, уже проверенный учеными.

Приложение довольно популярно, оно open-source, исходный язык — С. В результате выбор был сделан в пользу seqtk.

Согласно Makefile, вот то, что нужно: Перед преобразованием в Wasm стоит посмотреть принцип компиляции seqtk для десктопа.

# Compile to binary
$ gcc seqtk.c \ -o seqtk \ -O2 \ -lm \ -lz

В принципе, скомпилировать seqtk можно при помощи Emscripten. Если его нет, обходимся образом Docker.

$ docker pull robertaboukhalil/emsdk:1.38.26
$ docker run -dt --name wasm-seqtk robertaboukhalil/emsdk:1.38.26

При желании собрать его можно и самостоятельно, но на это нужно время.

Внутри контейнера без проблем можно взять emcc в качестве альтернативы gcc:

# Compile to WebAssembly
$ emcc seqtk.c \ -o seqtk.js \ -O2 \ -lm \ -s USE_ZLIB=1 \ -s FORCE_FILESYSTEM=1

Изменений минимум:

Вместо вывода в бинарный файл Emscripten для генерации файлов используется .wasm и .js, который применяется для запуска модуля WebAssemby.

Библиотека распространена и портирована на WebAssembly, а Emscripten включает ее в проект. Для поддержки библиотеки zlib используется флаг USE_ZLIB.

Это POSIX-подобная ФС, работающая в оперативной памяти внутри браузера. Активируется виртуальная файловая система Emscrippten. Когда страница обновляется, память очищается.

Чтобы понять, зачем нужна виртуальная файловая система, стоит сравнить способ запуска seqtk из командной строки со способом запуска скомпилированного модуля WebAssembly.

# On the command line
$ ./seqtk fqchk data.fastq # In the browser console
> Module.callMain(["fqchk", "data.fastq"])

Получение доступа к виртуальной файловой системе нужно, чтобы не переписывать seqtk под строковый, а не файловый ввод. В этом случае фрагмент данных отображается как файл data.fastq в виртуальной ФС с вызовом на нем main() seqtk.

Вот новая архитектура:

Этот способ дает возможность выполнять вычисления в фоновом потоке, не ухудшая отзывчивость браузера. Рисунок демонстрирует, что вместо вычислений в основном потоке браузера используется WebWorkers. Ну а контроллер WebWorker запускает Worker, управляя его взаимодействием с основным потоком.

После завершения выполнения Worker выдает результат в виде Promise. Команда seqtk запускается при помощи Worker на примонтированном файле. И так в несколько итераций. Когда сообщение получено главным потоком, результат используется для обновления графиков.

Что насчет производительности WebAssembly?

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

При использовании решения «из коробки» прирост производительности составил девять раз.

Дело в том, что большое количество результатов QC-анализа не используется seqtk, поэтому их можно удалить. Это отличный результат, но, как оказалось, есть возможность оптимизировать и его. Если это сделать, результат по сравнению с JS улучшается в 13 раз.

Достичь его удалось простым комментированием команд printf().

Дело в том, что на этом этапе fastq.bio получает результаты анализа с вызовом разных функций С. Но и это не все. Любая из них вычисляет свой набор характеристик, так что каждый фрагмент файла читался по два раза.

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

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

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

Skillbox рекомендует:

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть