asm.js — это подмножество JavaScript , разработанное для того, чтобы позволить компьютерному программному обеспечению, написанному на таких языках, как C, запускаться в качестве веб-приложений , сохраняя при этом характеристики производительности, значительно превосходящие стандартный JavaScript , который является типичным языком, используемым для таких приложений.
asm.js состоит из строгого подмножества JavaScript, в которое код, написанный на статически типизированных языках с ручным управлением памятью (например, C), транслируется компилятором исходного кода, таким как Emscripten (основанным на LLVM ). [2] Производительность повышается за счет ограничения языковых функций теми, которые поддаются предварительной оптимизации и другим улучшениям производительности.
Mozilla Firefox был первым веб-браузером, реализовавшим специфичные для asm.js оптимизации, начиная с версии 22. [3]
asm.js заменен на WebAssembly .
asm.js обеспечивает значительное повышение производительности веб-приложений , но не нацелен на повышение производительности написанного вручную кода JavaScript и не обеспечивает ничего, кроме повышения производительности.
Предполагается, что характеристики производительности будут ближе к характеристикам нативного кода, чем стандартного JavaScript, за счет ограничения языковых возможностей теми, которые поддаются предварительной оптимизации и другим улучшениям производительности. [4] Благодаря использованию подмножества JavaScript, asm.js в значительной степени поддерживается всеми основными веб-браузерами , [5] в отличие от альтернативных подходов, таких как Google Native Client .
asm.js обычно не пишется напрямую: вместо этого, как промежуточный язык, он генерируется с помощью компилятора , который берет исходный код на таком языке, как C++ , и выводит asm.js.
Например, рассмотрим следующий код на языке C:
int f ( int i ) { return i + 1 ; }
Emscripten выведет следующий JS-код:
функция f ( i ) { i = i | 0 ; возврат ( i + 1 ) | 0 ; }
Обратите внимание на добавление |0
и отсутствие спецификаторов типа. В JavaScript побитовые операторы преобразуют свои операнды в 32-битные целые числа со знаком и выдают целочисленные результаты. Это означает, что побитовое ИЛИ с нулем преобразует значение в целое число (очень простое «концептуальное» представление побитовых операторов может вообще не иметь дела с преобразованием типов, но каждый язык программирования определяет операторы для собственного удобства, как это делает здесь Javascript). Делая это для каждого параметра, это гарантирует, что если функция вызывается из внешнего кода, значение будет преобразовано в правильный тип. Это также используется для возвращаемого значения, в данном случае для того, чтобы гарантировать, что результат добавления 1 к i будет целым числом (иначе он может стать слишком большим), и для обозначения возвращаемого типа функции. Эти преобразования требуются asm.js, чтобы оптимизирующий компилятор мог заранее создавать высокоэффективный собственный код. В таком оптимизирующем компиляторе не выполняется никаких преобразований, когда код asm.js вызывает другой код asm.js, поскольку требуемые спецификаторы типов гарантируют, что значения уже будут иметь правильный тип. Более того, вместо выполнения сложения с плавающей точкой и преобразования в целое число, он может просто выполнить собственную целочисленную операцию. Вместе это приводит к значительному повышению производительности.
Вот еще один пример расчета длины строки:
size_t strlen ( char * ptr ) { char * curr = ptr ; while ( * curr != 0 ) { curr ++ ; } return ( curr - ptr ); }
Это приведет к следующему коду asm.js:
функция strlen ( ptr ) { ptr = ptr | 0 ; var curr = 0 ; curr = ptr ; while (( MEM8 [ curr >> 0 ] | 0 ) != 0 ) { curr = ( curr + 1 ) | 0 ; } return ( curr - ptr ) | 0 ; }
В сгенерированном коде переменная MEM8 на самом деле представляет собой побайтовое «представление» типизированного буфера, который служит «кучей» кода asm.js.
Поскольку asm.js работает в браузере, производительность сильно зависит как от браузера, так и от оборудования. Предварительные тесты программ на C, скомпилированных в asm.js, обычно в пределах 2 раз медленнее, чем нативная компиляция с Clang . [6]
Большая часть этого прироста производительности по сравнению с обычным JavaScript обусловлена 100% согласованностью типов и фактически отсутствием сборки мусора (память управляется вручную в большом типизированном массиве). Эта более простая модель без динамического поведения, без выделения или освобождения памяти, только с узким набором четко определенных целочисленных и плавающих операций обеспечивает гораздо большую производительность и потенциал для оптимизации . [ необходима цитата ]
Тест производительности Mozilla от декабря 2013 года показал значительные улучшения: «Firefox с оптимизацией float32 может выполнять все эти тесты примерно в 1,5 раза медленнее, чем нативный, или лучше». [7] Mozilla указывает, что производительность нативно скомпилированного кода — это не единичный показатель, а скорее диапазон, при этом различные нативные компиляторы (в данном случае Clang и GCC ) предоставляют код с разной производительностью. «На самом деле, в некоторых тестах производительности, таких как Box2D , FASTA и copy, asm.js так же близок или ближе к Clang, чем Clang к GCC. В одном случае asm.js даже немного превосходит Clang на Box2D». [7]
Проект Emscripten предоставляет инструменты, которые можно использовать для компиляции кодовых баз C и C++ (или любых других языков, которые можно преобразовать в LLVM IR ) в asm.js. [2]
Все браузеры с поддержкой ECMAScript 6 должны иметь возможность запускать код asm.js, поскольку он является подмножеством этой спецификации. Однако, поскольку в этой редакции были добавлены функции для обеспечения полной поддержки asm.js ( Math.fround()
), старые браузеры, в которых отсутствуют эти функции, могут столкнуться с проблемами.
Некоторые реализации браузеров специально оптимизированы для asm.js:
Почти все текущие приложения на основе asm.js — это приложения C/C++, скомпилированные в asm.js с помощью Emscripten или Mandreel. Учитывая это, приложения, которые в ближайшем будущем будут нацелены на asm.js, — это те, которые выиграют от переносимости работы в браузере, но имеют уровень сложности, для которого прямой перенос на JavaScript будет невозможен.
На сегодняшний день уже перенесено множество языков программирования , прикладных фреймворков , программ , библиотек , игр , игровых движков и другого программного обеспечения . [10] Некоторые из них приведены ниже.
asm.js в основном устарел с появлением WebAssembly (wasm), который имеет формат байт-кода, который быстрее анализируется. [38] Попытки расширить JavaScript более низкоуровневыми функциями, такими как SIMD.js, также были приостановлены с 2017 года. [39]
asm.js остается полезным в первую очередь как «запасной вариант» для wasm, через программу, написанную организацией WebAssembly, которая преобразует wasm в asm.js. Не существует специального конвертера из asm.js в wasm, но компиляторы TypeScript в wasm могут быть частично использованы. [40] Ссылка на WebAssembly emitter binaryen использовалась для содержания asm2wasm
модуля, но была удалена после того, как Emscripten прекратил его использовать. [41]
AssemblyScript, который компилирует TypeScript в Binaryen IR; wasm2js, который компилирует WebAssembly в JS
v97: Удален asm2wasm, который поддерживал бэкэнд fastcomp Emscripten, после удаления fastcomp.(См. также PR#3042.)