2KB项目,专业的源码交易网站 帮助 收藏 每日签到

Asm.js: Javascript的编译目标

  • 时间:2019-01-23 18:38 编辑:2KB 来源:2KB.COM 阅读:301
  • 扫一扫,手机访问
  • 分享
摘要:
asm.js 英文原文:Asm.js: The JavaScript Compile Target

正如许多开发者一样,我也为Asm.js的前景而感到兴奋不已。最近的新闻——Asm.js正在被Firefox支持——引起了我的兴趣。同样感兴趣的还有Mozilla和Epic声明(mirror)他们已经为Asm.js而支持Unreal Engine 3——并且运行十分良好。

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

由于Unreal Engine 3开始支持Asm.js,我浏览了来自Twitter,blogs和其他地方的回应,当一部分开发者表现出对这项制造奇迹的技术跃跃欲试时,我也看到许多疑惑:Asm.js是一个插件吗?Asm.js能让我平时使用的Javascript运行的更快吗?它兼容所有浏览器吗?从这些回应中,我认为Asm.js及其相关技术很重要,我将解释这些技术,好让开发者明白发生了什么以及他们将会如何从这些技术中获益。另外,为了写出这篇我对这项技术的概览,我也请教了David Herman(Mozilla研究院的高级研究员)一大堆关于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 的代码做了一些渲染工作,那么它几乎总是由WebGL来处理的 (并且由 OpenGL 来渲染). 这样技术上就利用了JavaScript 和浏览器的好处,但几乎完全避开了页面中Javascript使用的实际的、常规的代码执行和渲染路径.

Asm.js是Javascript的一个子集,它深度的限制了其所能做的范围和所能操作的对象。这样做才能使得Asm.js的代码能够尽可能运行的快,从而尽可能减少各种假定情况的出现,从而能够把Asmjs代码直接转变成为汇编代码。有一个特别值得注意的是 - Asmjs还只是Javascript - 不需要浏览器的插件或者是别的特性去运行它(虽然一个能够检测出并且优化Asmjs代码的浏览器当然是要快一些)。它是Javascript的一个特定的子集,为性能优化而生,特别是为那些需要编译成为Javascript的应用程序来做优化。

最佳的理解Asmjs工作的方式,就是看看一些Asmjs-编译化的代码。让我们看看这个函数,这是从真实的Asmjs编译模块里面提取出来的函数(来自BananaBread demo)。我对代码格式做了调整,所以看起来更加合乎代码片段的阅读,原本它是一个高度压缩的JavaScript代码中的一个很大的计算机二进制对象。

function Vb(d) {
d = d | 0;
var e = 0, f = 0, h = 0, j = 0, k = 0, l = 0, m = 0, n = 0,
o = 0, p = 0, q = 0, r = 0, s = 0;
e = i;
i = i + 12 | 0;
f = e | 0;
h = d + 12 | 0;
j = c[h >> 2] | 0;
if ((j | 0) > 0) {
c[h >> 2] = 0;
k = 0
} else {
k = j
}
j = d + 24 | 0;
if ((c[j >> 2] | 0) > 0) {
c[j >> 2] = 0
}
l = d + 28 | 0;
c[l >> 2] = 0;
c[l + 4 >> 2] = 0;
l = (c[1384465] | 0) + 3 | 0;
do {
if (l >>> 0 < 26) {
if ((4980736 >>> (l >>> 0) & 1 | 0) == 0) {
break
}
if ((c[1356579] | 0) > 0) {
m = d + 4 | 0;
n = 0;
while (1) {
o = c[(c[1356577] | 0) + (n << 2) >> 2] | 0;
do {
if (a[o + 22 | 0] << 24 >> 24 == 24) {
if (!(Vp(d, o | 0) | 0)) {
break
}
p = (c[m >> 2] | 0) + (((c[h >> 2] | 0) - 1 | 0) * 40 & -1) + 12 | 0;
q = o + 28 | 0;
c[p >> 2] = c[q >> 2] | 0;
c[p + 4 >> 2] = c[q + 4 >> 2] | 0;
c[p + 8 >> 2] = c[q + 8 >> 2] | 0;
c[p + 12 >> 2] = c[q + 12 >> 2] | 0;
c[p + 16 >> 2] = c[q + 16 >> 2] | 0;
c[p + 20 >> 2] = c[q + 20 >> 2] | 0;
c[p + 24 >> 2] = c[q + 24 >> 2] | 0
}
} while (0);
o = n + 1 | 0;
if ((o | 0) < (c[1356579] | 0)) {
n = o
} else {
break
}
}
r = c[h >> 2] | 0
} else {
r = k
} if ((r | 0) == 0) {
i = e;
return
}
n = c[j >> 2] | 0;
if ((n | 0) >= 1) {
i = e;
return
}
m = f | 0;
o = f + 4 | 0;
q = f + 8 | 0;
p = n;
while (1) {
g[m >> 2] = 0.0;
g[o >> 2] = 0.0;
g[q >> 2] = 0.0;
Vq(d, p, f, 0, -1e3);
n = c[j >> 2] | 0;
if ((n | 0) < 1) {
p = n
} else {
break
}
}
i = e;
return
}
} while (0);
if ((c[1356579] | 0) <= 0) {
i = e;
return
}
f = d + 16 | 0;
r = 0;
while (1) {
k = c[(c[1356577] | 0) + (r << 2) >> 2] | 0;
do {
if (a[k + 22 | 0] << 24 >> 24 == 30) {
h = b[k + 14 >> 1] | 0;
if ((h - 1 & 65535) > 1) {
break
}
l = c[j >> 2] | 0;
p = (c[1384465] | 0) + 3 | 0;
if (p >>> 0 < 26) {
s = (2293760 >>> (p >>> 0) & 1 | 0) != 0 ? 0 : -1e3
} else {
s = -1e3
} if (!(Vq(d, l, k | 0, h << 16 >> 16, s) | 0)) {
break
}
g[(c[f >> 2] | 0) + (l * 112 & -1) + 56 >> 2] = +(b[k + 12 >> 1] << 16 >> 16 | 0);
h = (c[f >> 2] | 0) + (l * 112 & -1) + 60 | 0;
l = k + 28 | 0;
c[h >> 2] = c[l >> 2] | 0;
c[h + 4 >> 2] = c[l + 4 >> 2] | 0;
c[h + 8 >> 2] = c[l + 8 >> 2] | 0;
c[h + 12 >> 2] = c[l + 12 >> 2] | 0;
c[h + 16 >> 2] = c[l + 16 >> 2] | 0;
c[h + 20 >> 2] = c[l + 20 >> 2] | 0;
c[h + 24 >> 2] = c[l + 24 >> 2] | 0
}
} while (0);
k = r + 1 | 0;
if ((k | 0) < (c[1356579] | 0)) {
r = k
} else {
break
}
}
i = e;
return
}

从技术上说,这是Javascript代码,但是我们已经看到这段代码一点都不像大多我们正常看到的操作DOM的Javascript。通过看这段代码,我们可以发现几件事:

  • 这段特定的代码只能处理数值。事实上这是所有Asm.js代码的情形。Asm.js只能处理被挑出的几种不同的数值类型,而没有提供其他的数据类型(包括字符串,布尔型和对象)。

  • 所有外部数据在一个称为堆的对象中存储并被引用。堆在本质上是一个大数组(应当是一个在性能上高度优化的类型化数组)。所有的数据在这个数组中存储——有效的替代了全局变量,结构体,闭包和其他形式的数据存储。

  • 当访问和赋值变量时,结果被统一的强制转换成一种特定类型。例如f = e | 0;给变量f赋值e,但它也确保了结果的类型是一个整数(|0把值转换成整数,确保了这点)。这也发生在浮点型上——注意0.0和g[...] = +(...)的使用.

  • 看看从数据结构中写入读出的值,用c表示的数据结构好像是一个Int32Array(存储32位整数,因为这些值总是通过|0被转换成或者转换自一个整数),g好像是一个Float32Array(存储32位浮点数,因为这些值总是通过包裹上+(...)转换成浮点数)。

这么做以后,结果就是高度优化,并且可以直接从Asm.js语法转换成汇编,而不必像常常要对Javascript做的那样解释它。它有效地削减了使像Javascript之类的的动态语言缓慢的东西:例如需要垃圾收集器和动态类型。

作为一个更容易理解的Asm.js代码示例,我们来看看一个Asm.js规范上的例子:

function DiagModule(stdlib, foreign, heap) {
    "use asm";

    // Variable Declarations
    var sqrt = stdlib.Math.sqrt;

    // Function Declarations
    function square(x) {
        x = +x;
        return +(x*x);
    }

    function diag(x, y) {
        x = +x;
        y = +y;
        return +sqrt(square(x) + square(y));
    }

    return { diag: diag };
}

看看这个模块,它完全能让人理解!阅读这些代码,我们能更好的理解Asm.js模块的结构。一个模块包含在一个函数中,它以顶部的"use asm";指令开始。它提示解释器这个函数里所有的东西可以被当成Asm.js处理,并可以直接被编译成汇编代码。

注意在函数顶部的三个参数:stdlib,foreigh和heap。stdlib对象包含了很多内建数学函数的引用。foreign提供了自定义用户功能(例如在WebGL中绘制图形)的访问。最后,heap给了你一个ArrayBuffer,它可以通过很多透镜(例如Int32Array和Float32Array)来观察。

该模块剩下的被分成了三部分:变量声明,函数声明,还有最后把函数导出暴露给用户的一个对象。

导出是尤其要去理解的一个重点,因为它既让所有模块中的代码被当成Asm.js处理,又使得代码可以被其他普通的Javascript代码使用。因此,在理论上你可以通过使用上面的DiagModule代码,写下如下代码:

document.body.onclick = function() {
    function DiagModule(stdlib){"use asm"; ... return { ... };}

    var diag = DiagModule({ Math: Math }).diag;
    alert(diag(10, 100));
};

这带来了一个Asm.js模块DiagModule,它被Javascript解释器特殊处理,但仍然能够被其他Javascript代码使用(我们仍能访问并使用它,比如一个单击事件处理程序)。

性能如何?

现在Asm.js唯一的实现就是 nightly versions of Firefox(而且也只是针对特定的几个平台)。原来的数字告诉我们Ams.js的性能是非常非常不错的。对于复杂的应用(比如上面的游戏)性能仅仅比普通C++编译的慢两倍(可以和Java或者C#相媲美)。实际上,这已经比目前浏览器的运行时环境要快很多了,几乎是最新版的Firefox或者Chrome执行速度的4~10倍。

基于目前最好测试,可以看出Asm.js在性能上有很大的提升。考虑到现在仅仅是Asm.js的最初开发阶段,相信在不久的将来就会有更大的性能提升。

看到Asm.js和当前的Firefox和Chrome引擎的性能差距是很有意思的。一个4~10倍的性能差异是非常巨大的(就好像拿这些浏览器和IE6做性能对比一样)。有趣的是虽然有这么大的性能差异,但是许多的Asm.js演示例子仍然是可以在Chrome和Firefox——这些代表着当前Javascript先进技术的引擎——上使用的。这也就是说他们的性能明显不如一个运行着优化过的Asm.js代码的浏览器相提并论。

其它翻译版本 (1) 加载中

使用情况

需要说明的是现在几乎所有基于Asm.js的应用都是C/C++应用使用Emscripten编译的。可以肯定的说,在不久的将来,这类即将运行在Asm.js的应用,将会从可以在浏览器中运行这一可移植性中获益,但是在支持javascript方面有一定复杂度的应用将不可行。

到目前为止,大部分的使用情况下,代码性能是至关重要的:比如运行游戏,图像,处理语言翻译和库。从一个关于Emscripten项目列表的概览可以看到许多即将被广大开发者使用的技术。

  • 许多游戏引擎已经被支持。一个好的演示也许就是BananaBread FPS游戏(源代码),它可以直接在浏览器中运行,支持多玩家和机器人。

  • 支持javascript的LaTeX,即textlive.js,使用Emscripten,允许你完全在你的浏览器中编辑PDF文档。

  • 支持javascript的SQLite,能够在Node.js中运行。

  • NaCL:一个网络加密解密的库。

Asm.js支持

就像之前提到的那样,现在只有nightly版本的Firefox支持Asm.js的优化。

但是,要注意Asm.js格式的Javascript代码仍然是Javascript,虽然其存在一些限制。这样,其他的浏览器即使不支持Asm.js仍可以将其作为普通的Javascript代码运行。

有关代码性能重要而令人不解的一点是:如果浏览器不支持typed array或者不能对Asm.js代码进行特殊的编译,Asm.js的性能会变的很差。当然,这并不止针对Asm.js,如果没有这些特性,浏览器的性能在其他方面也会收到影响。

Asm.js与Web开发

你可能已经看出来了,上面的Asm.js代码不是手工输入的的。Asm.js需要一些特殊的工具来编写,而且其开发和编写普通的Javascript代码有很大区别。目前一般的Asm.js应用都是从C/C++编译到Javascript的,很显然它们都不会与DOM进行任何交互,而是直接与WebGL打交道。

为了能让一般的开发者使用,需要一些更能让人接受的中间语言。目前LLJS已经逐渐实现向Asm.js编译了。需要注意的是,LLJS这样的语言同样与常规的Javascript有很大区别,会让许多Javascript开发者感到困扰。即使是用LLJS这样更加友好的语言,编写Asm.js必须要对复杂的代码进行优化,这恐怕只有资深开发者能够胜任了。

就算有了LLJS或者别的语言来帮助我们编写Asm.js,我们也没有同样性能优异的DOM可以使用。理想的环境应该是将LLJS与DOM一起编译产生单一的可执行二进制文件。我还想不出这样做性能会有多好,但是我想这么做!

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。


2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务

  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【计算机/互联网|】Nginx出现502错误(2020-01-20 21:02)
【计算机/互联网|】网站运营全智能软手V0.1版发布(2020-01-20 12:16)
【计算机/互联网|】淘宝这是怎么了?(2020-01-19 19:15)
【行业动态|】谷歌关闭小米智能摄像头,因为窃听器显示了陌生人家中的照片(2020-01-15 09:42)
【行业动态|】据报道谷歌新闻终止了数字杂志,退还主动订阅(2020-01-15 09:39)
【行业动态|】康佳将OLED电视带到美国与LG和索尼竞争(2020-01-15 09:38)
【行业动态|】2020年最佳AV接收机(2020-01-15 09:35)
【行业动态|】2020年最佳流媒体设备:Roku,Apple TV,Firebar,Chromecast等(2020-01-15 09:31)
【行业动态|】CES 2020预览:更多的流媒体服务和订阅即将到来(2020-01-08 21:41)
【行业动态|】从埃隆·马斯克到杰夫·贝佐斯,这30位人物定义了2010年代(2020-01-01 15:14)
联系我们

Q Q: 7090832

电话:400-0011-990

邮箱:7090832@qq.com

时间:9:00-23:00

联系客服
商家入住 服务咨询 投拆建议 联系客服
0577-67068160
手机版

扫一扫进手机版
返回顶部