WebAssembly与程序编译
Webassembly(WASM)和CSS的Grid布局⼀样都是⼀个新东西,Chrome从57开始⽀持。在讲wasm之前我们先看代码是怎么编译的成机器码,因为计算机只认识机器码。
1. 机器码
计算机只能运⾏机器码,机器码是⼀串⼆进制的数字,如下⾯的可执⾏⽂件a.out:
上⾯显⽰成16进制,是为了节省空间。
例如我⽤C写⼀个函数,如下:
int main(){
int a = 5;
int b = 6;
int c = a + b;
return 0;
}
int main(){
int a = 5;
int b = 6;
int c = a + b;
return 0;
}
然后把它编译成⼀个可执⾏⽂件,就变成了上⾯的a.out。a.out是⼀条条的指令组成的,如下图所⽰,研究⼀下为了做⼀个加法是怎么进⾏的:
第⼀个字节表⽰它是哪条指令,每条指令的长度可能不⼀样。上⾯总共有四条指令,第⼀条指令的意思是把0x5即5这个数放到内存内置为[rbp –
0x8]的位置,第⼆条指令的意思是把6放到内存地址为[rbp –
0xc]的位置,为什么内存的位置是这样呢,因为我们定义了两个局部变量a和b,局部变量是放在栈⾥⾯的,⽽new出来的是放在内存堆⾥⾯的。上⾯main函数的内存栈空间如下所⽰:
rbp是⼀个base
pointer,即当前栈的基地址,这⾥应该为main函数⼊⼝地地址,然后⼜定义了两个局部变量,它们依次⼊栈,栈由下往上增长,向内存的低位增长,在我的这个Linux操作系统上是这样的。最后return返回的时候这个栈就会⼀直pop到⼊⼝地址位置,回到调它的那个函数的地址,这样你就知道函数栈调⽤是怎么回事了。
⼀个栈最⼤的空间为多少呢?可以执⾏ulimit -s或者ulimit -a命令,它会打印出当前操作系统的内存栈最⼤值:
> ulimit -a
stack size              (kbytes, -s) 8192
这⾥为8Mb,相对于⼀些OS默认的64Kb,已经是⼀个⽐较⼤的值了。⼀旦超出这个值,就会发⽣栈溢出stack overflow.
理解了第⼀条指令和第⼆条指令的意思后就不难理解第三条和第四条了。第三条是把内存地址为[rbp – 8]放到ecx寄存器⾥⾯,第四条做⼀个加法,把[rbp – 12]加到ecx寄存器。就样就完成了c = a + b的加法。
更多汇编和机器码的运算读者有兴趣可以⾃⾏去查资料继续扩展,这⾥我提了⼀下,帮助读者理解这种⽐较较陌⽣的机器码是怎么回事,也是为了下⾯讲解WASM.
2. 编译和解释
我们知道编程语⾔分为两种,⼀种是编译型的如C/C++,另⼀种是解释型如Java/Python/JS等。
在编译型语⾔⾥⾯,代码需经过以下步骤转成机器码:
先把代码⽂本进⾏词法分析、语法分析、语义分析,转成汇编语⾔,其实解释型语⾔也是需要经过这些步骤。通过词法分析识别单词,例如知道了var是⼀个关键词,people这个单词是⾃定义的变量名字;语法分析把单词组成了短句,例如知道了定义了⼀个变量,写了⼀个赋值表达式,还有⼀个for循环;⽽语义分析是看逻辑合不合法,例如如果赋值给了this常量将会报错。
再把汇编再翻译成机器码,汇编和机器码是两个⽐较接近的语⾔,只是汇编不需要去记住哪个数字代表哪个指令。
编译型语⾔需要在运⾏之前⽣成机器码,所以它的执⾏速度⽐较快,⽐解释型的要快若⼲倍,缺点是由于它⽣成的机器码是依赖于那个平台的,所以可执⾏的⼆进制⽂件⽆法在另⼀个平台运⾏,需要再重新编译。
相反,解释型为了达到⼀次书写,处处运⾏(write once, run evrywhere)的⽬的,它不能先编译好,只能在运⾏的时候,根据不同的平台再⼀⾏⾏解释成机器码,导致运⾏速度要明显低于编译型语⾔。
如果你看Chrome源码的话,你会发现V8的解释器是⼀个很复杂的⼯程,有200多个⽂件:
然后来讲WebAssembly了。
3. WebAssembly介绍
汇编table指令什么意思WASM的意义在于它不需要JS解释器,可直接转成汇编代码(assembly code),所以运⾏速度明显提升,速度⽐较如下:
通过⼀些实验的数据,JS⼤概⽐C++慢了7倍,ASM.js官⽹认为它们的代码运⾏效率是⽤clang编译的代码的1/2,所以就得到了上⾯⽐较粗糙的对⽐。
Mozilla公司最开始开发asm.js,后来受到Chrome等浏览器公司的⽀持,慢慢发展成WASM,W3C还有⼀个专门的社区,叫WebAssembly Community Group。
强类型的,并且只⽀持整数、浮点数、函数调⽤、数组、算术计算,如下使⽤asm规范写的代码做两数的加WASM是JS的⼀个⼦集,它必须是强类型
法:
function () {
"use asm";
function add(x, y) {
x = x | 0;
y = y | 0;
return x | 0 + y | 0;
}
return {add: add};
}
function () {
"use asm";
function add(x, y) {
x = x | 0;
y = y | 0;
return x | 0 + y | 0;
}
return {add: add};
}
正如asm.js官⽹提到的:
strictly-typed integers, floats, arithmetic, function calls, and An extremely restricted subset of JavaScript that provides onlystrictly-typed
heap accesses
WASM的兼容性,如caniuse所⽰:
最新的主流浏览器基本上已经⽀持。
4. WASM Demo
(1)准备
Mac电脑需要安装以下⼯具:
cmake make Clang/XCode
Windows需要安装:
cmake make VS2015 以上
然后再装⼀个
WebAssembly binaryen (asm2Wasm)
(2)开始
写⼀个add.asm.js,按照asm规范,如下图所⽰:
然后再运⾏刚刚装的⼯具asm2Wasm,就可以得到⽣成的wasm格式的⽂本,如下图所⽰
可以看到WASM⽐较接近汇编格式,可以⽐较⽅便地转成汇编。
如果不是在控制台输出,⽽是输出到⼀个⽂件,那么它是⼆进制的。运⾏以下命令: