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