В этой статье я объясню, насколько легко реализовать 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 захватывает стек вызовов, как показано ниже.
Ссылка
- AssemblyScript Github
- Wasm-loader
- Webpack: Как написать загрузчик
- Настоящее и будущее WebAssembly - я настоятельно рекомендую вам не торопиться и прочитать эти шесть статей.