Asm.js

asm.js是一个中间语言，设计目的是使采用C等编程语言编写的计算机软件可运行为网络应用程序，同时性能特征明显优于标准JavaScript。

asm.js包括一个JavaScript的严格子集，其中的代码采用具有手动内存管理的静态类型语言（就像C语言）编写，代码使用一个源代码至源代码编译器（例如基于LLVM的Emscripten）翻译。通过将语言特性限制在适合提前优化和其他性能改进的范围内，性能得到了提高。

Mozilla Firefox是第一个实现针对asm.js优化的浏览器，从Firefox 22开始使用。

## 设计

asm.js由JavaScript语言的一个严格子集组成。它可以显著提高采用具有手动内存管理（例如C）的静态类型语言编写的网络应用程序在使用源代码至源代码编译器转换为JavaScript后的性能。Asm.js的目标并不是提高手写JavaScript代码的性能，也不实现增强性能以外的其他目的。

通过将语言特性限制在适合提前优化和其他性能改进的范围内，其旨在具有比标准JavaScript更接近于本地（原生）代码的性能特征。通过使用JavaScript的一个子集，asm.js很大程度上支持所有主要的网页浏览器，这不同于WebAssembly或Google Native Client等途径。

## 代码生成

asm.js通常不直接编写，而是作为一种通过编译器生成的中间语言，该编译器获取C++或其他语言的源代码，然后输出asm.js。

例如，提供下列C语言代码：

```c
int f(int i) {
  return i + 1;
}
```

Emscripten将输出下列JavaScript代码：

```ecmascript
function f(i) {
  i = i|0;
  return (i + 1)|0;
}
```

注意新增的`|0`和去除的类型说明符。在JavaScript中，按位运算符会将操作数转换为32位有符号整数并给出整数结果。这意味着使用0的按位OR为一个无作用的操作，只是将值转换为整数。通过对每个参数这样做，确保了从外部代码调用该函数时，该值被转换为正确的类型。这也用于返回值，在该情况下确保添加1到i的结果将是一个整数（否则可能变得太大），并标记函数的返回类型。这些转换为asm.js所必需，这样优化编译器才可以提前生成高效的本地代码。在此类优化编译器中，当asm.js代码调用其他asm.js代码时，转换不执行，因为必需的类型说明符意味着已保证值具有正确的类型。此外，不同于执行浮点加法和转换为整数，它可以简单地执行本机整数运算。这样一来，它可以得到显著的性能增益。

下面是另一个计算字符串长度的例子：

```c
size_t strlen(char *ptr) {
  char *curr = ptr;
  while (*curr != 0) {
    curr++;
  }
  return (curr - ptr);
}
```

它对应以下asm.js代码：

```ecmascript
function strlen(ptr) {
  ptr = ptr|0;
  var curr = 0;
  curr = ptr;
  while (MEM8[curr]|0 != 0) {
    curr = (curr + 1)|0;
  }
  return (curr - ptr)|0;
}
```

在生成的代码中，变量MEM8实际上是一个类型缓冲区的逐字节“视图”，它充当asm.js代码的堆（heap）。

## 性能

因为asm.js在浏览器中运行，所以性能很大程度上取决于浏览器和硬件。编译为asm.js的C程序的初步基准通常比使用Clang的本地编译慢一倍以上。

这种超过普通JavaScript的性能增益主要是由于100％的类型一致性以及几乎没有垃圾回收（内存是手动管理的大型类型数组）。这个更简单的模型没有动态行为，没有内存分配或释放，只有一组简单、定义明确的整数和浮点操作，从而可实现更好的性能和优化潜力。

Mozilla在2013年12月的基准测试显示：“使用float32优化的Firefox可以运行所有基准，只比原生速度慢不到1.5倍。Mozilla指出本地编译代码的性能不是单个度量，而是一个范围，使用不同的本地编译器（此例中为Clang与GCC）将提供不同性能的代码。“事实上，在一些基准测试比如Box2D、FASTA和copy中，asm.js与Clang比较接近，或者比Clang到GCC还接近Clang。在一种情况下，asm.js甚至在Box2D上略微击败Clang。”

## 实现

Emscripten项目提供了可以编译C和C++（或其他任何可转换为LLVM IR的语言）代码为asm.js的工具。

所有支持JavaScript较新版本的浏览器都应该能支持运行asm.js代码，因为它是该规范的子集。

部分浏览器的实现针对asm.js进行了特别优化：

- Mozilla Firefox是第一个实现针对asm.js优化的网页浏览器，自Firefox 22开始使用。OdinMonkey是Mozilla在Firefox中使用的asm.js提前编译器，它是IonMonkey（SpiderMonkey的JIT编译器）的一个组件。
- 微软在Microsoft Edge使用的JavaScript引擎Chakra中实现了asm.js支持，执行验证以产生高度优化的JIT代码。
- Google Chrome的V8 JavaScript引擎在Chrome 28中对asm.js基准测试的性能是以前Chrome版本的两倍以上，尽管Chrome的V8没有使用提前编译。

## 采用

目前几乎所有基于asm.js的应用程序都是使用Emscripten或Mandreel编译为asm.js的C/C++应用程序。

到目前为止，已有不少编程语言、应用程序框架、程序、函式库、游戏、游戏引擎及其他软件已被移植。部分名单见下：

### 编程语言

- C/C++：Clang和LLVM
- Lua VM：Lua 虚拟机器
- Perl：(micro)perl-5.16.3的移植
- Python – CPython移植
- Ruby – Ruby移植

### 应用程序框架

- pepper.js：许多PNaCl应用的移植
- Qt：various Qt demos及KDE应用（如Kate）的移植

### 程序和库

- OpenGL、SDL和SDL2
- Vim（Vi IMproved）
- FreeType：在JavaScript中使用FreeType的TrueType字体光栅化
- SQLite
- GNU Privacy Guard
- ctags
- gnuplot
- Graphviz
- zlib

### 游戏引擎

- 虚幻引擎3: 在4天内移植
- Unreal Engine 4
- Unity
- ScummVM，它支持许多经典的冒险游戏
- Godot

### 游戏

- Doom：运行在PrBoom上的开源Freedoom游戏assets，基于开源的Doom代码
- SuperTux
- Dune II via OpenDune
- BananaBread based on Cube 2
- Humble Mozilla Bundle中的每个游戏（Super Hexagon、轻率漠视重力、Osmos、Zen Bound 2、Dustforce DX、Voxatron、FTL: Advanced Edition和Democracy 3）

### 仿真器

- EM-DOSBox：一个DOSBox模拟器的移植
- Start9.io：面向多个游戏架构的网络仿真平台
- JSMESS：一个MESS模拟器的移植，面向许多游戏机和计算机系统

### 数学计算

- HTML5 Fractal Playground – 绘制迭代函数生成的分形，例如Mandelbrot fractal。

## 参见

- WebAssembly – 一个开发中的用于浏览器的字节码，旨在比asm.js更快地解析RPython
- CrossBridge
- Google Native Client（NaCl）

## 外部链接

- dherman/asm.js
- Asm.js: The JavaScript Compile Target
- RPerl
- Asm.js usage per Google Chrome statistics

## 基本资料

- 受影响于：JavaScript
- 操作系统：平台无关
- 发行日期：2013年3月21日
- 日语原文：asm.js
- 设计者：Mozilla