Введение в WebAssembly (WASM)

WebAssembly был анонсирован в 2015 году. Первой демонстрацией стала реализация игры Unity Angry Bots в Firefox, Google Chrome и Microsoft Edge. Спустя всего 4 года после анонса WebAssembly был признан официальным веб-стандартом и четвертым веб-языком после HTML, CSS и JavaScript, а на сегодняшний день его поддерживают 94% браузеров. 

WebAssembly обеспечивает скорость исполнения в браузере, близкую к нативной, что позволяет переносить в интернет десктопные приложения (например, AutoCAD) и даже видеоигры (например, Doom 3).

Похоже, WebAssembly удалось привлечь к себе всеобщее внимание. Самое время поговорить о нем.

  • Что не так с JavaScript?
  • Что такое WebAssembly?
  • Это просто новый язык программирования, как C и C++?
  • Как работает WebAssembly?
  • Станет ли WASM будущим веб-приложений?

Мы ответим на все эти вопросы и познакомим вас с WebAssembly. Его потенциал способен изменить привычные нам сайты до неузнаваемости!

Что не так с JavaScript?

JavaScript был разработан Бренданом Эйхом в 1995 году для браузера Netscape. Он давал возможность добавлять некоторые взаимодействия к статичным веб-страницам тех дней.

JavaScript  —  интерпретируемый язык программирования с динамической типизацией. Язык является динамически типизированным, если тип переменной проверяется во время выполнения. В чем же проблема с таким типом языка? Посмотрим, что будет, если объявить переменную в C++, который является статически типизированным языком.

int x = 5 ;

Компилятор определяет тип и место в памяти для x. И все это укладывается в одну инструкцию. Однако для такого же присваивания в JavaScript движок должен каждый раз при выполнении программы проверять, является ли переменная целым числом, float или любым другим допустимым типом данных. Таким образом, каждая инструкция в JavaScript должна пройти через несколько проверок типов и преобразований, что замедляет ее выполнение.

Ниже показано, на что JavaScript тратит время в процессе выполнения кода.

Javascript

А теперь посмотрим, на что тратит время WebAssembly.

WebAssembly

В WebAssembly упрощен весь процесс компиляции, что делает его более быстрым по сравнению с JavaScript.

JavaScript не был разработан для высокопроизводительных приложений, требующих большой нагрузки от процессора.


Что такое WebAssembly?

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

Ниже приведен пример суммирующей функции, написанной на C++ и преобразованной в WASM.

Схема VirtualProcessor, преобразующего код C++ в двоичный код, понятный браузеру.

Это просто новый язык программирования, как C и C++?

Нет, это не язык программирования. Это технология, которая преобразует код, написанный на каком-либо языке программирования, в машинный код, понятный браузеру.

WASM (аббревиатура от WebAssembly) был разработан как целевая платформа для компиляции других языков, позволяющая компилировать серверный код (например, код на C или C++) и выполнять его внутри браузера.


Как работает WebAssembly?

Что такое язык ассемблера (assembly language) и ассемблер (assembler)?

  • Каждый процессор имеет свою архитектуру, например x86 или ARM. Кроме того, процессоры могут понимать только машинный код.
  • Написание машинного кода утомительно, поэтому для данной архитектуры/процессора существует язык ассемблера.
  • Ассемблер преобразует инструкции на языке ассемблера в машинный код, понятный процессору.

Вот как приложения, написанные на языке C, работают на компьютере.

В WebAssembly, как и в универсальном ассемблере, код, написанный на языке высокого уровня, таком как C++, преобразуется в машинный код, понятный браузеру.


Начало работы с WebAssembly

WebAssembly  —  это просто файл с расширением WASM. Его можно рассматривать как модуль, который может быть импортирован в JavaScript-программу.

Взаимодействие файлов в каталоге проекта

Помните: WASM не может взаимодействовать с DOM напрямую. Поэтому вам нужно использовать как JavaScript, так и WASM.

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

1. Напишите приложение на предпочитаемом языке

Напишем для примера небольшую функцию на C++, которая находит n-ое число Фибоначчи.

// Ниже приведена функция, написанная на C++, которая находит n-ое число Фибоначчи
int fib(int n)
{
if (n <= 1)
return n;
return fib(n-1) + fib(n-2);
}

2. Создайте модуль WASM

Теперь нужно преобразовать файл C++ в предварительно скомпилированный WASM-модуль, который будет понятен браузеру.

Существуют различные способы преобразования кода на языке высокого уровня в WASM. В этом руководстве будет использован Web Assembly Explorer.

Шаг 1: скопируйте и вставьте код на C++ и кликните на компиляцию (compile).

Шаг 2: кликните на ассемблер (assembler).

Шаг 3: загрузите файл WASM.

После нажатия на компиляцию и ассемблер (1-й и 2-й шаги)

Скопируйте и вставьте скачанный файл в каталог проекта под названием “math. wasm”.

3. Дистрибутируйте модуль  —  в идеале, используя CDN для низкой задержки (в данном случае будем запускать WASM-файл локально)

Создайте файл script.js и пока оставьте его пустым.

Каталог проекта должен выглядеть следующим образом.

Вид каталога проекта

4. Загрузите модуль WASM

Мы создадим функцию loadWebAssembly(), которая преобразует заданный файл в объект ArrayBuffer. Затем этот двоичный ArrayBuffer может быть преобразован в модуль WebAssembly. Необходимость создания объекта ArrayBuffer связана с памятью веб-ассемблера. Экземпляр этого модуля может быть прочитан браузером.

script.js должен выглядеть следующим образом.

let math;
// Создадим функцию loadWebAssembly, которая преобразует данный файл в двоичный ArrayBuffer.
function loadWebAssembly(fileName) {
return fetch(fileName)
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.compile(buffer)) // Buffer converted to Web Assembly
.then(module => {return new WebAssembly.Instance(module) }); // Instance of Web assmebly module is returened
};

// Мы вызываем функцию для math.wasm для данного экземпляра.
loadWebAssembly('math.wasm')
.then(instance => {

});

5. Создайте экземпляр модуля

Теперь перейдем к самой сложной части  —  нам нужно ссылаться на функции, созданные на C++, в JS-файле. Но напрямую ссылаться на эти функции нельзя. Придется использовать имена, которые генерируются в файле WASM. Эти имена записаны в колонке WAT в Web Assembly Explorer.

Используйте имя функции, выделенное на следующей иллюстрации.

Переменная, на которую нужно ссылаться, выделена

В вашем WAT-файле эти имена будут другими.

1-я часть в script.js посвящена загрузке файла WASM.

2-я часть содержит несколько простых функций Javascript, созданных для сравнения производительности Javascript и WebAssembly.

//---------------------------Часть 1--------------------------------------------------------
// Создадим функцию loadWebAssembly, которая преобразует данный файл в двоичный ArrayBuffer.
function loadWebAssembly(fileName) {
return fetch(fileName)
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.compile(buffer))
.then(module => {return new WebAssembly.Instance(module) });
};

// Вызываем функцию для math.wasm
loadWebAssembly('math.wasm')
.then(instance => {
fibc = instance.exports._Z3fibi;

console.log('Call your functions !');
});

//---------------------------PART 2 -----------------------------------------------
// Функция, написанная на Javascript, для числа Фибоначчи
function fibj(n)
{
if (n <= 1)
return n;
return fibj(n-1) + fibj(n-2);
}

// Эта функция дает время, необходимое для выполнения функции C++
function perfoc(n){
var startTime = performance.now()

var c=fibc(n)

var endTime = performance.now()

console.log(`Calculating nth Fibonacci with WASM took ${endTime - startTime} milliseconds,nth fibonacci is ${c}`)

}

// Эта функция дает время, необходимое для функции Javascript
function perfoj(n){
var startTime = performance.now()

var j=fibj(n)

var endTime = performance.now()

console.log(`Calculating nth Fibonacci with JS took ${endTime - startTime} milliseconds, nth fibonacci is ${j}`)

}

6. Вызовите функции экземпляра

Теперь для завершения работы загрузите сайт на localhost!

Примечание: вы не можете запустить index.html напрямую, так как он не загрузит модуль WASM. Используйте что-то вроде расширения Live Server в Visual Studio Code или Xampp, чтобы переместить каталог проекта на localhost.

Теперь перейдите в консоль, чтобы вызвать следующие две функции.

  1. fibj( )→ написана на простом Javascript.
  2. fibc( )→ написана на C++ и затем преобразована в WebAssembly.
Две функции fibc() и fibj()

Ниже приводится сравнение между функцией fibj(), написанной только на JavaScript, и функцией fibc(), импортированной из WebAssembly.

WebAssembly работает молниеносно

Время выполнения fibj( ) и fibc( ) измеряется с помощью функций:

  1. perfoj() → измеряет время выполнения fibj();
  2. perfoc() → измеряет время выполнения yfibc().

Как видно в приведенных выше GIF, время выполнения fibj()(написанной на JavaScript) больше, чем время выполнения fibc()(написанной с использованием WebAssembly).


Станет ли WebAssembly будущим веб-приложений?

С помощью WebAssembly можно разрабатывать высокоэффективные веб-приложения, которые будут работать с производительностью, близкой к нативной. WebAssembly позволит выполнять такие задачи, как обработка видео, 3D-рендеринг, мультимедийные игры, криптографические вычисления и live-приложения AR/VR.

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

Посмотреть проект можно здесь.

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Chandrashekhar Dongre: Introduction to WebAssembly (WASM)

Предыдущая статьяКак запросить датафрейм Pandas с помощью SQL
Следующая статьяРаспознавание речи с помощью Python