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

В настоящее время существует множество языков программирования, и разные языки имеют разные плюсы и минусы. Что касается языков, с которыми я работал, я бы оценил C / C ++ ›Java› JavaScript с точки зрения производительности и JavaScript ›Java› C / C ++ с точки зрения производительности, хотя это будет зависеть от программиста. В мире JavaScript появление Just-in-Time (JIT) Compiler стимулировало быстрое развитие и стало основой, на которой программисты создавали все более крупные веб-приложения. Однако, хотя все еще не удовлетворены производительностью JavaScript, появилась WebAssembly.

Два аспекта, которые имеют наибольшее значение в производительности, возникают, когда происходит компиляция (язык компиляции против языка интерпретатора) и отсутствие необходимости в виртуальной машине (ВМ), поскольку код WebAssembly может выполняться непосредственно в сборке. Как известно большинству из вас, JavaScript, благодаря гибкости и производительности, имеет свои запатентованные плюсы и минусы. Он интерпретируется, компилируется и выполняется во время выполнения, поэтому с точки зрения производительности такие жертвы очевидны.

Способы преобразования существующих модулей в WebAssembly

Существуют наборы инструментов, которые позволяют преобразовать любой код asm.js или C / C ++, написанный для FireFox и работающий в нем, в модули WebAssembly, подобные показанным ниже.

  • Создайте код asm.js и превратите его в WebAssembly (binaryen)
  • Код C / C ++ и превратите его в WebAssembly (Emscripten)

Однако для Front-end разработчика еще далеко до быстрых и производительных модулей WebAssembly. Чтобы наделить вашу программу великолепной WebAssembly, вам необходимо пройти и понять запутанные концепции, включая make, llvm, C / C ++, компиляцию и компоновку. Затем вы можете использовать Webpack или накопительный пакет для интеграции созданных модулей в среду разработки. Неудивительно, что большая часть вводного материала по WebAssembly включает только создание основной функции на C / C ++ для печати «Hello, World».

Очень вероятно, что вы будете измотаны, прежде чем начнете применять знания в полезных приложениях.

AssemblyScript

Есть некоторые предварительные требования к языкам, которые можно преобразовать в WebAssembly, и это то, что типы переменных можно классифицировать. Такие языки, как C / C ++, Rust, TypeScript (Проблема с целью компиляции, отличной от JavaScript) могут быть преобразованы в WebAssembly, а AssemblyScript, нижний набор TypeScript, также может быть преобразован. в WebAssembly.

Причина выбора AssemblyScript - это служебная программа. Он зарегистрирован на NPM, и фронтенд-разработчик может легко протестировать коды WebAssembly.

В этой статье я буду использовать AssemblyScript для преобразования своих кодов в модуль WebAssembly.

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

  • Четко задокументируйте тип, чтобы избежать неявных изменений формата.
  • Необходимо инициализировать значения параметров по умолчанию.
  • Поддерживаются только четкие типы (не поддерживаются любые или неопределенные типы.)
  • Логические операторы && и || всегда приводят к значениям типа bool.

Если вы устанавливаете Assemblyscript через NPM, вы можете легко скомпилировать и создать файл .wasm (WebAssembly) через командную строку. Кроме того, существуют инструменты для использования созданных файлов WASM в качестве модулей, таких как assemblyscript-loader и wasm-loader, и они в основном откладываются тем, что первый имеет больше возможностей для загрузки модулей WASM изнутри кода, а последний предоставляется с помощью загрузчика Webpack, и его легче объединить и использовать.

AssemblyScript стал проще

Как мы упоминали ранее, использование собственных языков, таких как C / C ++, для компиляции в WebAssembly - чрезвычайно запутанная задача. Давайте воспользуемся семейством языков JavaScript, которое более знакомо, для написания кодов, которые могут компилироваться непосредственно в WebAssembly и которые могут быть объединены с помощью Webpack.

Ассемблерный скрипт-live-загрузчик

Два пакета NPM в некоторой степени предлагают удобство, но все же есть недостатки, когда дело доходит до написания кода JavaScript в среде разработки Webpack, который нужно немедленно связать. Поэтому я написал свой собственный загрузчик Webpack. Загрузчик предоставляет две основные функции.

  • Компиляция AssemblyScript как WASM
  • Создание модуля WASM для использования в качестве WebAssembly.Module

Примечание. При использовании wasm-loader произошла ошибка, связанная с искажением кода, поэтому мне пришлось самому написать загрузочную часть WASM.

Установка пакета

Поскольку я еще не зарегистрировал пакет в NPM, установите его через Github.

npm install --save-dev https://github.com/dongsik-yoo/assemblyscript-live-loader.git

Конфигурация загрузчика Webpack

Измените файл конфигурации, чтобы позволить Webpack объединять файлы, написанные на AssemblyScript.

webpack.config.js

module: {
  loaders: [
    {
      test: /\.asc$/, // assemblyscript Source File
      exclude: "/node_modules/",
      loader: "assemblyscript-live-loader"
    }
  ];
}

Написание кода для компиляции как WebAssembly на AssemblyScript

Создайте файл ./asc/Calculator.asc и заполните следующее содержимое.

export function add(a: int, b: int): int {
  return a + b;
}
export function subtract(a: int, b: int): int {
  return a - b;
}
export function multiply(a: int, b: int): int {
  return a * b;
}
export function divide(a: int, b: int): int {
  return a / b;
}

Импорт модуля

Теперь якобы сложный процесс компиляции, загрузки и связывания модуля можно упростить с помощью загрузчика Webpack. Теперь давайте импортируем модуль WebAssembly для использования в нашем файле index.js и вызовем подготовленные тестовые функции для получения результатов.

import Calculator from "./asc/Calculator.asc";
const calc = new Calculator().exports;
const add = calc.add(44, 8832);
const subtract = calc.subtract(100, 20);
const multiply = calc.multiply(13, 4);
const divide = calc.divide(20, 4);
console.log(add);
console.log(subtract);
console.log(multiply);
console.log(divide);

Строить

npx webpack

Примечание. Будет удобнее использовать недавно добавленный npx в NPM 5.2.

Тестирование производительности JavaScript с помощью WebAssembly

Прежде чем я начал писать эту статью, у меня сложилось впечатление, что WebAssembly в целом будет быстрее. Поэтому я написал простые бинарные арифметические операторы и факториалы, используя версию WebAssembly, которая скомпилировала JavaScript и AssemblyScript для простого измерения производительности. Однако результаты тестирования производительности WebAssembly оказались не такими, как я ожидал.

Примечание. Chrome и Firefox теперь используют WebAssembly по умолчанию и могут быть протестированы.

Тестовая среда

  • Инструменты тестирования: Chrome59.0.3071.115, Firefox 54.0.1, микро-бенчмарк
  • Результаты тестирования (График): TUI-Chart 2.9.0
  • Протестированные функции: сложение, вычитание, умножение, деление, факториал
  • Обозначения: JavaScript (красный), WebAssembly (оранжевый) Более короткие гистограммы показывают более высокую производительность.
  • Страница тестирования: Вышеупомянутый тест задокументирован здесь.
  • Тестовый код: тестовые коды можно увидеть по соответствующим ссылкам. Javascript, WebAssembly, Бенчмарк

Образец тестового кода

// Initialization Code 
const factorialNumber = 1000;
const factorialLoop = 10000;
const N = 1000000;
...
// (JavaScript) Loops are executed in JavaScript
{
    name: 'Factorial',
    fn: function () {
        var i = 0;
        for (; i < factorialLoop; i += 1) {
            CalculatorJS.factorial(factorialNumber);
        }
    }
},
// (JavaScript) Loops are executed in the test function.
{
    name: 'Factorial',
    fn: function () {
        CalculatorJS.factorialWithLoopCount(factorialLoop, factorialNumber);
    }
},
// (WebAssembly) Loops are executed in JavaScript
{
    name: 'Factorial',
    fn: function () {
        var i = 0;
        for (; i < factorialLoop; i += 1) {
            CalculatorWASM.factorial(factorialNumber);
        }
    }
},
// (WebAssembly) Loops are executed in the test function. 
{
    name: 'Factorial',
    fn: function () {
        CalculatorWASM.factorialWithLoopCount(factorialLoop, factorialNumber);
    }
}
// Javascript Test Code
function factorial(num) {
  let tmp = num;
  if (num < 0) {
    return -1;
  } else if (num === 0) {
    return 1;
  }
  while (num > 2) {
    tmp *= num;
    num -= 1;
  }
  return tmp;
}
function factorialWithLoopCount(count, num) {
  let i = 0;
  for (; i < count; i += 1) {
    factorial(num);
  }
}
// WebAssembly Test Code
export function factorial(num: int): int {
  var tmp: int = num;
  if (num < 0) {
    return -1;
  } else if (num === 0) {
    return 1;
  }
  while (num > 2) {
    tmp *= num;
    num -= 1;
  }
  return tmp;
}
export function factorialWithLoopCount(count: int, num: int) {
  var i: int = 0;
  for (; i < count; i += 1) {
    factorial(num);
  }
}

Результаты тестирования из Chrome

Результаты тестирования из Firefox

Результаты тестирования и заключительные замечания

Как многие предсказывали, WebAssembly не заменит JavaScript, а вместо этого будет использоваться для повышения производительности модулей, которые могут выполняться быстрее, чем простой JavaScript.

На производительность JavaScript влияют невероятно разнообразные факторы. Как я упоминал ранее, я наверняка предположил, что WebAssembly будет быстрее, но, помимо факториальной операции, базовые двоичные арифметические операции выполнялись быстрее с помощью JavaScript.

Чтобы перечислить несколько из множества факторов, влияющих на производительность JavaScript, можно следующим образом.

  • Производительность движка JavaScript
  • Неожиданно быстрая оптимизация производительности JIT
  • Обработка стека вызовов (например, ›при использовании рекурсии)
  • Стоимость вызова функций WebAssembly
  • Оптимизация компилятора WebAssembly

Я должен предположить, что в этом тесте, когда я запускал повторяющиеся циклы, простые арифметические операторы становились быстрее, поскольку они были скомпилированы в сборку в JIT. Кроме того, похоже, что процесс преобразования из JavaScript в WebAssembly, также известный как батут, серьезно сказался на производительности. Наконец, нельзя игнорировать разницу между Chrome и Firefox.

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

И остальное

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

Ссылка