asm.js 入门教程

正如许多开发者一样,我也为Asm.js的前景而感到兴奋不已。

获得一个C++游戏引擎运行Javascript,并使用WebGL来渲染,这是一个重大的突破,这个突破很大程度上归功于Mozilla开发的工具链,才使得这一切变得可能。

2012年,Mozilla 的工程师 Alon Zakai 在研究 LLVM 编译器时突发奇想:许多 3D 游戏都是用 C / C++ 语言写的,如果能将 C / C++ 语言编译成 JavaScript 代码,它们不就能在浏览器里运行了吗?众所周知,JavaScript 的基本语法与 C 语言高度相似。

asm.js

Asm.js 是什么?

为了理解 Asm.js 及其适用与浏览器的所在,你需要知道它的由来以及它存在的意义。
Asm.js 来自于 JavaScript 应用的一个新领域: 编译成JavaScript的C/C++应用. 它是 JavaScript 应用的一个全新流派,由 Mozilla 的 Emscripten项目催生而来。
Emscripten 将 C/C++ 代码传入  LLVM, 并将 LLVM生成的字节码转换成 JavaScript (具体的, Asm.js, 是 JavaScript 的一个子集。

asm.js 架构

如果被编译成的 Asm.js 的代码做了一些渲染工作,那么它几乎总是由WebGL来处理的 (并且由 OpenGL 来渲染). 这样技术上就利用了JavaScript 和浏览器的好处,但几乎完全避开了页面中Javascript使用的实际的、常规的代码执行和渲染路径。

asm.js 的原理

C / C++ 编译成 JS 有两个最大的困难。

  • C / C++ 是静态类型语言,而 JS 是动态类型语言。
  • C / C++ 是手动内存管理,而 JS 依靠垃圾回收机制。

asm.js 就是为了解决这两个问题而设计的:它的变量一律都是静态类型,并且取消垃圾回收机制。除了这两点,它与 JavaScript 并无差异,也就是说,asm.js 是 JavaScript 的一个严格的子集,只能使用后者的一部分语法。

asm.js 简介

一旦 JavaScript 引擎发现运行的是 asm.js,就知道这是经过优化的代码,可以跳过语法分析这一步,直接转成汇编语言。另外,浏览器还会调用 WebGL 通过 GPU 执行 asm.js,即 asm.js 的执行引擎与普通的 JavaScript 脚本不同。这些都是 asm.js 运行较快的原因。据称,asm.js 在浏览器里的运行速度,大约是原生代码的50%左右。
下面就依次介绍 asm.js 的两大语法特点。

静态类型的变量

asm.js 只提供两种数据类型。

  • 32位带符号整数
  • 64位带符号浮点数

JavaScript中的数据类型

其他数据类型,比如字符串、布尔值或者对象,asm.js 一概不提供。它们都是以数值的形式存在,保存在内存中,通过 TypedArray 调用。

如果变量的类型要在运行时确定,asm.js 就要求事先声明类型,并且不得改变,这样就节省了类型判断的时间。

asm.js 的类型声明有固定写法,变量 | 0表示整数,+变量表示浮点数。

var a = 1;
var x = a | 0;  // x 是32位整数
var y = +a;  // y 是64位浮点数

上面代码中,变量x声明为整数,y声明为浮点数。支持 asm.js 的引擎一看到x = a | 0,就知道x是整数,然后采用 asm.js 的机制处理。如果引擎不支持 asm.js 也没关系,这段代码照样可以运行,最后得到的还是同样的结果。
再看下面的例子。

// 写法一
var first = 5;
var second = first;
// 写法二
var first = 5;
var second = first | 0;

上面代码中,写法一是普通的 JavaScript,变量second只有在运行时才能知道类型,这样就很慢了,写法二是 asm.js,second在声明时就知道是整数,速度就提高了。
函数的参数和返回值,都要用这种方式指定类型。

function add(x, y) {
  x = x | 0;
  y = y | 0;
  return (x + y) | 0;
}

上面代码中,除了参数x和y需要声明类型,函数的返回值也需要声明类型。

垃圾回收机制

asm.js 没有垃圾回收机制,所有内存操作都由程序员自己控制。asm.js 通过 TypedArray 直接读写内存。
下面就是直接读写内存的例子。

var buffer = new ArrayBuffer(32768);
var HEAP8 = new Int8Array(buffer);
function compiledCode(ptr) {
  HEAP[ptr] = 12;
  return HEAP[ptr + 4];
}

如果涉及到指针,也是一样处理。

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

上面的代码编译成 asm.js,就是下面这样。

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

asm.js 与 WebAssembly 的异同

如果你对 JS 比较了解,可能知道还有一种叫做 WebAssembly 的技术,也能将 C / C++ 转成 JS 引擎可以运行的代码。那么它与 asm.js 有何区别呢?

回答是,两者的功能基本一致,就是转出来的代码不一样:asm.js 是文本,WebAssembly 是二进制字节码,因此运行速度更快、体积更小。从长远来看,WebAssembly 的前景更光明。

但是,这并不意味着 asm.js 肯定会被淘汰,因为它有两个优点:首先,它是文本,人类可读,比较直观;其次,所有浏览器都支持 asm.js,不会有兼容性问题。

asm.js 的用途

asm.js 不仅能让浏览器运行 3D 游戏,还可以运行各种服务器软件,比如 Lua、Ruby 和 SQLite。 这意味着很多工具和算法,都可以使用现成的代码,不用重新写一遍。

另外,由于 asm.js 的运行速度较快,所以一些计算密集型的操作(比如计算 Hash)可以使用 C / C++ 实现,再在 JS 中调用它们。

真实的转码实例可以看一下 gzlib 的编译,参考它的 Makefile 怎么写。

参考资料

  • asm.js, by Wikipedia
  • Emscripten & asm.js: C++'s role in the modern web, by Alon Zakai
  • Asm.js: The JavaScript Compile Target, by John Resig
  • WebAssembly: A New Hope, by Philipp Spiess and James Swift
  • Understanding asm.js, by Afshin Mehrabani

asm.js 入门教程

: » asm.js 入门教程

原创文章,作者:506227337,如若转载,请注明出处:https://blog.ytso.com/251277.html

(0)
上一篇 2022年5月2日
下一篇 2022年5月2日

相关推荐

发表回复

登录后才能评论