这篇博客文章是这一系列解释如何将Rust发射到地球以外的许多星系的文章的一部分:
- 前奏,
- WebAssembly 星系
- ASM.js星系(当前这一集)
- C 星系
- PHP星系,以及
- NodeJS 星系
Rust解析器将要探索的第二个星系是ASM.js。这篇文章会解释什么是ASM.js,怎样编译博客解析器到ASM.js以及如何在浏览器中和Javascript一起使用ASM.js. 使用ASM.js的目标是当作WebAssembly不可用的备用方案。我强烈建议你读读前一篇关于WebAssembly的文章,因为他们有很多共同的地方
#什么是ASM.js,为什么需要ASM.js
Web应用的主要语言是Javascript,任何想要运行在Web上的应用都必须编译成Javascript,比如游戏。但是这里有个问题:编译输出文件非常重(即使是WebAssembly),这样Javascript虚拟机将很难对这样的代码做优化,这就导致运行缓慢或者执行低效(可以考虑用游戏的规模来做例子)。而且在Javascript为编译目标的情况下,一些语言基础设施变得毫无用处,比如eval
如果有一种新的语言可以作为编译目标,而且也能被Javascript虚拟机运行会怎么样?这就是WebAssembly,但是在2013年的时候解决方案就是ASM.js:
asm.js, 是一个严格的Javascript子集,它能够被用作编译器输出的底层的,高效的目标语言。这个子语言高效的描述>了一个沙盒虚拟机,可以适用于内存不安全的语言,像C或者C++。静态和动态的组合校验让Javascript可以对有效 的asm.js代码使用一种叫做ahead-of-time(AOT)的编译时优化策略。
因此ASM.js程序其实只是一个普通的Javascript程序。它不是一个新的语言而是Javascript的一个子集。它可以被任何Javascript虚拟机执行。但是,有个特殊的魔法声明use asm;,会指示虚拟机用ASM.js引擎来优化这个程序。
ASM.js通过算术运算引入了类型作为标记系统。比如,x|0表示x是一个整数,+x表示x是一个双精度小数,fround(x)表示x是一个浮点数。下面的例子声明了一个函数fn increment(x: u32) -> u32:
function increment(x) {x = x | 0;
return (x + 1) | 0;
}
另一个重要的区别是ASM.js以模块的方式来运行,以和Javascript隔离。这个模块是一个需要3个参数的函数:
- stdlib,一个带有引用到标准库API的对象
- foreign,一个带有用户定义功能的对象(比如通过WebSocket发送一些东西)
- heap,一个表示内存的数组(因为内存是手动管理的)
但是它仍然是Javascript。因此一个好消息是如果你的虚拟机没有对ASM.js做优化,那么它运行起来就是普通的Javascript程序,如果有优化,那么你就可以得到不错的性能提升。
这个图片展示了3个基准测试,分别对于不同的Javascript引擎:Firefox, Firefox+asm.js, Google, 和Native。
记住ASM.js是被设计为一种编译目标。因此一般你不需要特别关心,因为那是编译器的活。对把C或者C++编译到Web的一个典型的编译和执行流程如下
Emscripten,如上图所示,是一个在这个Web平台演进过程中非常重要的一个项目。
它是一个用来编译输出asm.js和WebAssembly的工具链,基于LLVM之上,能够让C和C++程序以接近原生应用的速 度运行在Web上,而且不需要任何插件。
如果你和ASM.js或者WebAssembly打交道,迟早有一天你会看到这个名字。I will not explain deeply what ASM.js is with a lot of examples. I recommend instead to read Asm.js: The Javascript Compile Target by John Resig, or Big Web app? Compile it! by Alon Zakai. 我不会用大量的例子来深入的解释ASM.js.我推荐两本书:Asm.js: The Javascript Compile Target by John Resig, 或者 Big Web app? Compile it! by Alon Zakai.
我们的过程有点不一样。我们不会直接编译Rust代码到ASM.js,而是先编译为WebAssembly,然后再编译为ASM.js。
#Rust