
正文
转:HTML5标准与性能之四:asm.js
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
HTML5标准与性能之四:asm.js
Cong Liu (Intel) 于 星期五, 24/05/2013 - 01:13 提交
之前的几篇文章分别介绍了WebWorkers、Typed Array和ParallelArray,最后,我们再来介绍一下与性能相关的标准:asm.js。
asm.js
asm.js是由Mozilla提出的一个基于JS的语法标准,主要是为了解决JS引擎的执行效率问题,尤其是使用Emscripten从C/C++语言编译成JS的程序的效率,目前只有Mozilla的Firefox Nightly中支持。
Emscripten
Emscripten是Mozilla的一个实验性项目,目的是把C/C++开发的应用编译成JS或HTML5的应用,编译过程中需要首先把C/C++程序编译成LLVM的中间代码,然后再转换成JS代码,这样做的主要原因是可以很好地复用现有的针对LLVM的优化。
C/C++是一种强类型的语言,这很好地对应了不同字长的CPU指令,每一种数据类型都有固定的上下限,一旦计算超出这个上下限就会产生溢出,比如
char
类型的取值范围是[-128,127],而
(char)(127 + 1)
这个计算就会溢出,得到
(char)-128
。
JS语言不仅是弱类型的,而且数值类型只有一种-
Number
,
Number
类型的数据采用双精度64位格式的IEEE 754值表示。如果用JS模拟C/C++类型的数值计算,就要模拟各种类型数据计算时的溢出效果。我们通过下面这个简单的C程序来看看如何用JS来模拟这样的计算。
1
char xInt8 = 127;
2
char yInt8 = xInt8 + 1;
// 溢出:yInt8 == (char) -128
3
cahr zInt8 = xInt8 / 2;
// 舍入:zInt8 == (char) 63
上面这段代码通过JS模拟后的代码如下:
1
var
xInt8 = 127;
// (1)
2
var
$add = (xInt8 + 1) | 0;
// (2)
3
var
yInt8 = ($add << 24) >> 24;
// (3)
4
var
$div = ((yInt8 | 0) / 2) & -1;
// (4)
5
var
zInt8 = ($div << 24) >> 24;
// (5)
-
8位整型变量127,相当于
char xInt8 = 127;
-
X|0
让计算结果成为32位整数,此时
$add == 128
-
先左移24位再右移24位,让第8位成为32位整数的符号位,来模拟8位整数计算,此时
yInt8 == -128
-
JS中
127/2
结果是浮点数63.5,利用
X&-1
将结果转化成32位整数63(-1的补码表示就是0xFFFFFFFF)
-
用上面(3)相同的方法,把结果转换成8位整数
大家看到,利用一些位移和逻辑运算可以模拟C/C++语言中的数据计算,Emscripten就利用这个方法将C代码转换成JS代码。大家可能还记得前篇文章介绍过的Typed Array,对Typed Array元素赋值则会自动进行相应的溢出和舍入处理,因此,利用Typed Array还可以改写成以下的代码:
1
var
HEAP8 =
new
Int8Array(STACK_SIZE);
// 构造一个Int8Array数组
2
HEAP8[0] = 127;
// char xInt8 = 127
3
HEAP8[1] = HEAP[0] + 1;
// char yInt8 = xInt8 + 1,此时yInt8是-128
4
HEAP8[2] = HEAP[1] / 2;
// char zInt8 = xInt8 / 2
在EmScripten中,Typed Array用来模拟C/C++中的堆栈以及指针的访问。
语法
asm.js不是一种新的语言,而是JS语法的一个子集,也就是说所有用asm.js写的程序都是合法的JS程序,asm.js与JS语言的关系有点类似C与C++的关系。因此,不支持asm.js的浏览器或JS引擎也可以无误地执行asm.js的代码。
asm.js顾名思义是作为JS的汇编语言来设计的,它的语法手写起来非常困难,且难以阅读。首先,asm.js的语法利用了一些
标注
让JS的变量成为强类型的,这些标注与Emscripten生成的代码如出一辙,实际上asm.js的产生就是为了提高Emscripten转换后的代码执行效率的。我们来看一个例子:
1
intValue = f1() | 0;
// 利用或运算(|)标记函数f1返回值为int32整数
2
floatValue = +f2();
// 用加号(+)标记函数f2返回值为双精度型浮点
同时,asm.js还规定了一个特殊的语法格式,下面这段代码是一个最简单的asm.js示例(
代码来自:http://asmjs.org/spec/latest/
):
1
function
MyAsmModule(stdlib, foreign, heap) {
2
"use asm"
;
// "use asm"来告诉JS引擎这个函数采用asm.js编译器解析执行
3
// module body...
4
return
{
// 返回向外暴露的函数接口
5
export1: f1,
6
export2: f2,
7
// ... };
8
}
9
var
result = MyAsmModule({}, {},
null
).export1();
// 调用函数export1
在这个例子中,参数stdlib、foreign、heap由外部传入,表示: 1. stdlib:有限的标准库函数,主要是一些数学函数,对应Math对象上的方法 2. foreign:foreign function interface (FFI)外部JS函数访问接口 3. heap:传入一个ArrayBuffer对象,作为asm.js的堆
编译和运行
由于asm.js相当于支持了强类型,因此可以直接对应编译成机器指令执行。asm.js的代码采用另外一套AOT(Ahead Of Time)编译器,将asm.js代码预先编译成机器指令,在编译过程或运行过程中,一旦发现语法错误或违反类型标记的情况出现,便重新将代码交予JS引 擎解析执行(见下图)。
图片来自:http://asmjs.org/spec/latest/
性能
JS一直以来被人诟病的一个方面就是它的性能,得益于这些年来浏览器之间的竞争,让JS的性能大大提升,Google的V8、Mozilla的 SpiderMonkey以及微软的Chakra在性能方面都已经相当不错,而asm.js进一步提升到相对本地代码2倍慢的性能(如下图)。这些测试用 例使用Emscripten转换而来,Emscripten已经可以直接生成asm.js代码。
数据来自:http://ejohn.org/blog/asmjs-javascript-compile-target/
Demo
-
Unreal Engine 3(虚幻游戏引擎): http://www.unrealengine.com/html5/
-
Bullet - 物理引擎:http://kripken.github.com/ammo.js/examples/new/ammo.html
参考
-
asm.js specification: http://asmjs.org/spec/latest/
-
IEEE 754: https://en.wikipedia.org/wiki/IEEE_floating_point
分类:
-
Microsoft Windows* 8 风格用户界面
-
优化
-
HTML5
-
JavaScript*
-
安卓*
-
HTML5
-
Tizen*
-
电话
-
开发人员
-
合作伙伴
-
教授
-
学生
-
安卓*
-
Apple iOS*
-
Apple Mac 操作系统 X*
-
Linux*
-
Microsoft Windows* 8
-
Tizen*
-
中级
标签:
-
asm.js
-
performance
-
emscripten
Cong Liu (Intel) 于 星期五, 24/05/2013 - 01:13 提交
之前的几篇文章分别介绍了WebWorkers、Typed Array和ParallelArray,最后,我们再来介绍一下与性能相关的标准:asm.js。
asm.js
asm.js是由Mozilla提出的一个基于JS的语法标准,主要是为了解决JS引擎的执行效率问题,尤其是使用Emscripten从C/C++语言编译成JS的程序的效率,目前只有Mozilla的Firefox Nightly中支持。
Emscripten
Emscripten是Mozilla的一个实验性项目,目的是把C/C++开发的应用编译成JS或HTML5的应用,编译过程中需要首先把C/C++程序编译成LLVM的中间代码,然后再转换成JS代码,这样做的主要原因是可以很好地复用现有的针对LLVM的优化。
C/C++是一种强类型的语言,这很好地对应了不同字长的CPU指令,每一种数据类型都有固定的上下限,一旦计算超出这个上下限就会产生溢出,比如
char
类型的取值范围是[-128,127],而
(char)(127 + 1)
这个计算就会溢出,得到
(char)-128
。
JS语言不仅是弱类型的,而且数值类型只有一种-
Number
,
Number
类型的数据采用双精度64位格式的IEEE 754值表示。如果用JS模拟C/C++类型的数值计算,就要模拟各种类型数据计算时的溢出效果。我们通过下面这个简单的C程序来看看如何用JS来模拟这样的计算。
1
|
char xInt8 = 127;
|
2
|
char yInt8 = xInt8 + 1;
// 溢出:yInt8 == (char) -128
|
3
|
cahr zInt8 = xInt8 / 2;
// 舍入:zInt8 == (char) 63
|
上面这段代码通过JS模拟后的代码如下:
1
|
var
xInt8 = 127;
// (1)
|
2
|
var
$add = (xInt8 + 1) | 0;
// (2)
|
3
|
var
yInt8 = ($add << 24) >> 24;
// (3)
|
4
|
var
$div = ((yInt8 | 0) / 2) & -1;
// (4)
|
5
|
var
zInt8 = ($div << 24) >> 24;
// (5)
|
-
8位整型变量127,相当于
char xInt8 = 127; -
X|0让计算结果成为32位整数,此时$add == 128 -
先左移24位再右移24位,让第8位成为32位整数的符号位,来模拟8位整数计算,此时
yInt8 == -128 -
JS中
127/2结果是浮点数63.5,利用X&-1将结果转化成32位整数63(-1的补码表示就是0xFFFFFFFF) - 用上面(3)相同的方法,把结果转换成8位整数
大家看到,利用一些位移和逻辑运算可以模拟C/C++语言中的数据计算,Emscripten就利用这个方法将C代码转换成JS代码。大家可能还记得前篇文章介绍过的Typed Array,对Typed Array元素赋值则会自动进行相应的溢出和舍入处理,因此,利用Typed Array还可以改写成以下的代码:
1
|
var
HEAP8 =
new
Int8Array(STACK_SIZE);
// 构造一个Int8Array数组
|
2
|
HEAP8[0] = 127;
// char xInt8 = 127
|
3
|
HEAP8[1] = HEAP[0] + 1;
// char yInt8 = xInt8 + 1,此时yInt8是-128
|
4
|
HEAP8[2] = HEAP[1] / 2;
// char zInt8 = xInt8 / 2
|
在EmScripten中,Typed Array用来模拟C/C++中的堆栈以及指针的访问。
语法
asm.js不是一种新的语言,而是JS语法的一个子集,也就是说所有用asm.js写的程序都是合法的JS程序,asm.js与JS语言的关系有点类似C与C++的关系。因此,不支持asm.js的浏览器或JS引擎也可以无误地执行asm.js的代码。
asm.js顾名思义是作为JS的汇编语言来设计的,它的语法手写起来非常困难,且难以阅读。首先,asm.js的语法利用了一些 标注 让JS的变量成为强类型的,这些标注与Emscripten生成的代码如出一辙,实际上asm.js的产生就是为了提高Emscripten转换后的代码执行效率的。我们来看一个例子:
1
|
intValue = f1() | 0;
// 利用或运算(|)标记函数f1返回值为int32整数
|
2
|
floatValue = +f2();
// 用加号(+)标记函数f2返回值为双精度型浮点
|
同时,asm.js还规定了一个特殊的语法格式,下面这段代码是一个最简单的asm.js示例( 代码来自:http://asmjs.org/spec/latest/ ):
1
|
function
MyAsmModule(stdlib, foreign, heap) {
|
2
|
"use asm"
;
// "use asm"来告诉JS引擎这个函数采用asm.js编译器解析执行
|
3
|
// module body...
|
4
|
return
{
// 返回向外暴露的函数接口
|
5
|
export1: f1,
|
6
|
export2: f2,
|
7
|
// ... };
|
8
|
}
|
9
|
var
result = MyAsmModule({}, {},
null
).export1();
// 调用函数export1
|
在这个例子中,参数stdlib、foreign、heap由外部传入,表示: 1. stdlib:有限的标准库函数,主要是一些数学函数,对应Math对象上的方法 2. foreign:foreign function interface (FFI)外部JS函数访问接口 3. heap:传入一个ArrayBuffer对象,作为asm.js的堆
编译和运行
由于asm.js相当于支持了强类型,因此可以直接对应编译成机器指令执行。asm.js的代码采用另外一套AOT(Ahead Of Time)编译器,将asm.js代码预先编译成机器指令,在编译过程或运行过程中,一旦发现语法错误或违反类型标记的情况出现,便重新将代码交予JS引 擎解析执行(见下图)。
图片来自:http://asmjs.org/spec/latest/
性能
JS一直以来被人诟病的一个方面就是它的性能,得益于这些年来浏览器之间的竞争,让JS的性能大大提升,Google的V8、Mozilla的 SpiderMonkey以及微软的Chakra在性能方面都已经相当不错,而asm.js进一步提升到相对本地代码2倍慢的性能(如下图)。这些测试用 例使用Emscripten转换而来,Emscripten已经可以直接生成asm.js代码。
数据来自:http://ejohn.org/blog/asmjs-javascript-compile-target/
Demo
- Unreal Engine 3(虚幻游戏引擎): http://www.unrealengine.com/html5/
- Bullet - 物理引擎:http://kripken.github.com/ammo.js/examples/new/ammo.html
参考
- asm.js specification: http://asmjs.org/spec/latest/
- IEEE 754: https://en.wikipedia.org/wiki/IEEE_floating_point
分类:
- Microsoft Windows* 8 风格用户界面
- 优化
- HTML5
- JavaScript*
- 安卓*
- HTML5
- Tizen*
- 电话
- 开发人员
- 合作伙伴
- 教授
- 学生
- 安卓*
- Apple iOS*
- Apple Mac 操作系统 X*
- Linux*
- Microsoft Windows* 8
- Tizen*
- 中级
标签:
- asm.js
- performance
- emscripten






