在 NodeJS 中体验 WebAssembly技术
2017-03-18 10:41
363 查看
你听说过 WebAssembly 吗?这是由 Google , Microsoft , Mozilla , Apple 等几家大公司合作发起的一个关于 面向Web的通用二进制和文本格式 的项目。
现在就让我们来看看WebAssembly到底是个啥?为什么它的出现和未来的发展跟我们每个人都息息相关,即使你并不是一个程序猿/媛~
至少在某种程度上,它将改变Web生态。
JavaScript–Web世界的汇编语言
我们有许多面向Web应用的开发规范,这些设计优良的规范让Web开发者们的工作变得更加的简单。我们很难想象自己所创建和网站或应用没有任何规则、编程语言、框架和开发理念可以遵循。而将所有这些事情组合到一起的Web规范有一个众所周知的名字: JavaScript !
JavaScript基本上已经成为了Web平台的标准开发语言。而随着越来越多的软件成为了Web应用,JavaScript更是获得了极大的发展。
但在过去几年,我们看到越来越多的项目问世,它们试图通过开发编译程序,将其他语言代码转化为 JavaScript,以此让开发者克服 JavaScript自身存在的一些短板。其中一些项目专注于给编程语言增加新的功能,比如微软的 TypeScript 和Google的 Dart ,或是加快
JavaScript的执行速度,例如 Mozilla 的 asm.js 项目和Google的 PNaCI 。
在默认环境下,JavaScript文档其实就是简单的文本文件,先是从服务器下载,然后由浏览器中的 JavaScript引擎解析并编译。用户可以通过Ajax技术在浏览网页时与服务器进行通信。
在浏览器端目前是使用JavaScript来实现与用户进行动态交互等功能,虽然很多JavaScript框架都致力于性能优化,但是一套基于字节码的系统仍然会有更快更好的性能表现。
所以,WebAssembly到底是个什么鬼?
WebAssembly是一种新的字节码格式。它的缩写是".wasm", .wasm为文件名后缀,是一种新的底层安全的二进制语法。。它被定义为“精简、加载时间短的格式和执行模型”,并且被设计为Web 多编程语言目标文件格式。 这意味着浏览器端的性能会得到极大提升,它也使得我们能够实现一个底层构建模块的集合,例如,强类型和块级作用域。(原文:
And it gives us access to a set of low level building blocks, such as a range of types and operations.这句话我实在不知如何翻译。。。) 不过别搞错了,这并不意味着WebAssmbly是为了取代 JavaScript而生哟~ 就像Bjarne Stroustup说的:“JS会活得很好,因为世界上只有两种类型的语言:一类语言被人们不断的地吐槽,而另一类语言压根儿没人用!”而 Eric
Elliott 认为:“最好不要把WebAssembly仅仅当做一门编程语言,实际上它更像是一个编译器。”
从asm.js到WebAssembly?
asm.js 是一个JavaScript的一个严格的子集,可以被用来作为一个底层的、高效的编译器目标语言。asm.js提供了一个类似于C/C++虚拟机的抽象实现,包括一个可有效负载和存储的大型二进制堆、整型和浮点运算、高阶函数定义、函数指针等。asm.js的思想是使用它所规定的方法来编写JavaScript代码,支持asm.js的引擎会将代码转变为十分高效的机器码。如果你是将C++代码编译为asm.js,将在浏览器端获得极大的性能提升。webassembly相较于asm.js的优势主要是涉及到性能方面。根据 WebAssembly
FAQ 的描述:在移动设备上,对于很大的代码库,asm.js仅仅解析就需要花费20-40秒,而 实验 显示WebAssembly的加载速度比asm.js快了20倍,这主要是因为相比解析
asm.js 代码,JavaScript引擎破译二进制格式的速度要快得多。
这玩意儿到底好在哪?
你很可能会问:“为啥所有人都在谈论WebAssembly?”这是因为WebAssembly对于JS来说绝对是一个巨大的改进,但我们常常会问自己:“这样,就够了吗?”当然不是,WebAssembly对于浏览器来说也有着非同一般的意义。 支持WebAssembly的浏览器可以识别二进制格式的文本,它有能力编译比JS文本小得多的二进制包。 这将给web应用带来类似与本地应用的性能体验!这四不四听起来很棒啊?!如果浏览器不得不解析完整的JS代码,这将会耗去好多时间(特别是在移动平台上),而浏览器对WebAssembly格式的解码速度显然要快得多得多得多:)下面献上JS作者BE大神的演讲视频地址(油管,需翻墙): Brendan
Eich on JavaScript Taking Both the High and Low Roads - O'Reilly Fluent 2014
都有谁入了WebAssembly的坑?
包括Google, Microsoft,Mozilla只是这一长串名单中的少数几家公司。项目带头人们发起了 WebAssemblyCommunity Group 这一社区,这个团队的愿景是“在一种新的,轻量的web编码格式的基础上,促进浏览器厂商们的合作.” 不过,WebAssembly项目还只是刚刚启动,虽然它有一个美妙的开头,但在WebAssembly成为一个大众认可的web标准之前,它还有很长的路要走。
为啥这玩意会影响每一个web开发者
因为webassembly让开发者有能力选择之前那些不能用来开发web应用的语言来进行web开发,或者他们也可以继续使用简单易用的JavaScript! W3CWebAssembly Community group给出了一些WebAssembly的用例,它们展示了WebAssembly如何使得web开发者更加轻松的工作:
一些执行效率更高的语言可以被编译成在Web平台上执行的代码。
提供了在浏览器端的开发者工具
更加快捷的企业级应用客户端(例如:数据库)
WebAssembly的用途很多。举几个栗子:WebAssembly可以被嵌入到已经开发好的JavaScript/HTML代码中;或者某款应用的主要框架可以使用 WebAssembly 模块(如动画、可视化和压缩等),而用户界面仍然可以主要使用 JavaScript/HTML语言编写。
精简的代码,更好的性能,更少的bug?
据WebAssembly的开发团队描述,使用WebAssembly意味着更少的原代码。与asm.js相比,它减少了大约25%的代码量。WebAssembly 作为一个门新的语言,已经得到了许多 Javascript 引擎的支持。WebAssembly的目标是为了让 C 和 C++ 这样的编译型语言更容易地在浏览器上运行。而让我感到激动的特性是计算性能和内存操作上的优化,这样让 Javascript 可以实现更为快速的浮点数计算而不用等到 TC39 方案的到来。在这里,借助于
NodeJS 我将会为你展示一些初级的 WebAssembly 示例。并进行一些基本的测试示例来显示其在性能方面的影响作用。
注:文中的所有只在 Node 7.2.1 中进行了测试,并开启了 --expose-wasm参数,其它环境可能无法运行。
通过开启 --expose-wasm参数,在 NodeJS 就可以访问全局对象 Wasm, 通过它可以来创建 WebAssembly 模块。
$ ~/Workspace/node-v7.2.1-linux-x64/bin/node --expose-wasm > Wasm { verifyModule: [Function], verifyFunction: [Function], instantiateModule: [Function], experimentalVersion: 11 } >
通过 Wasm.instantiateModule() 和 Uint8Array 来创建 WebAssembly 模块。
$ ~/Workspace/node-v7.2.1-linux-x64/bin/node --expose-wasm > Wasm.instantiateModule(new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x0b, 0x00, 0x00, 0x00])); {} >
为了创建一个最基本的 WebAssembly 模块,你需要传递一组 16进制的数据给 instaniateModule 来得到,如上创建的是一个最小的 WebAssembly 模块,因为每一个 .wasm 文件都必须以这一组16进制数据开始。
两个数字求和
有许多的编译工具可以直接将 C, C++ 和 Rust 代码编译成 WebAssembly,这样我们就不用手写字节码了。也存在一种名为'WebAssembly AST'(简称wast)的中间代码格式,如下为一个简单的支持两个参数的求和函数 wast 代码。(module (func $addTwo (param i32 i32) (result i32) (i32.add (get_local 0) (get_local 1))) (export "addTwo" $addTwo))
你可以使用这个在线工具将 wast 代码转换为 wasm 二进制代码。你也可以直接从这里下载
.wasm源码。
接下来如何用 Node.js 来运行 .wasm 文件呢?为了使用 .wasm文件,你需要通过文件模块将 .wasm 文件转换成 ArrayBuffer 格式。
const fs = require('fs'); const buf = fs.readFileSync('./addTwo.wasm'); const lib = Wasm.instantiateModule(toUint8Array(buf)).exports; // `Wasm` does **not** understand node buffers, but thankfully a node buffer // is easy to convert to a native Uint8Array. function toUint8Array(buf) { var u = new Uint8Array(buf.length); for (var i = 0; i < buf.length; ++i) { u[i] = buf[i]; } return u; } console.log(lib.addTwo(2, 2)); // Prints '4' console.log(lib.addTwo.toString()); // Prints 'function addTwo() { [native code] }'
上面的 addTwo 方法与原生的 Javascript 方法相比,性能如何呢?如下为我们的测试结果:
const fs = require('fs'); const buf = fs.readFileSync('./addTwo.wasm'); const lib = Wasm.instantiateModule(toUint8Array(buf)).exports; const Benchmark = require('benchmark'); const suite = new Benchmark.Suite; suite. add('wasm', function() { lib.addTwo(2, 2); }). add('js', function() { addTwo(2, 2); }). on('cycle', function(event) { console.log(String(event.target)); }). on('complete', function() { console.log('Fastest is ' + this.filter('fastest').map('name')); }). run(); function addTwo(a, b) { return a + b; } function toUint8Array(buf) { var u = new Uint8Array(buf.length); for (var i = 0; i < buf.length; ++i) { u[i] = buf[i]; } return u; }
$ ~/Workspace/node-v7.2.1-linux-x64/bin/node --expose-wasm ./addTwo.js 4 wasm x 43,497,742 ops/sec ±0.77% (88 runs sampled) js x 66,021,200 ops/sec ±1.28% (83 runs sampled) Fastest is js
阶乘
从上面的例子,我们可以看到 WebAssembly 并没有显示出性能上的优势。接下来我们进行阶乘计算来进一步的测试:(module (func $fac (param i32) (result i32) (if (i32.lt_s (get_local 0) (i32.const 1)) (then (i32.const 1)) (else (i32.mul (get_local 0) (call $fac (i32.sub (get_local 0) (i32.const 1))))))) (export "fac" $fac))
下面是计算 100!的测试比较结果。
const fs = require('fs'); const buf = fs.readFileSync('./factorial.wasm'); const lib = Wasm.instantiateModule(toArrayBuffer(buf)).exports; const Benchmark = require('benchmark'); const suite = new Benchmark.Suite; suite. add('wasm', function() { lib.fac(100); }). add('js', function() { fac(100); }). on('cycle', function(event) { console.log(String(event.target)); }). on('complete', function() { console.log('Fastest is ' + this.filter('fastest').map('name')); }). run(); function fac(n) { if (n <= 0) { return 1; } return n * fac(n - 1); } function toArrayBuffer(buf) { var ab = new ArrayBuffer(buf.length); var view = new Uint8Array(ab); for (var i = 0; i < buf.length; ++i) { view[i] = buf[i]; } return ab; }
$ ~/Workspace/node-v7.2.1-linux-x64/bin/node --expose-wasm ./factorial.js wasm x 2,484,967 ops/sec ±2.09% (87 runs sampled) js x 1,088,426 ops/sec ±2.63% (80 runs sampled) Fastest is wasm
这里我们可以看到,因为计算的复杂度上升,wasm 的优势就显示出来了。
下一步?
从上面的测试例子可以看到通过 WebAssembly 可以很好的优化JS代码,不过这里的测试例子还比较简单,不能覆盖很多的情况,同时 WebAssembly 还并没有推出稳定版本,所以不要莽撞的在应用中使用 WebAssembly。但是我们可以提前试用一下,尤其已经可以在NodeJS中试用了。长按图片识别图中二维码(或搜索微信公众号FrontEndStory)关注“前端那些事儿”,带你探索前端的奥秘。
相关文章推荐
- java web 轻量级开发全体验之一:JSP技术概述
- 未来web浏览技术提前体验:10个会让你惊叹不已的HTML5画布(canvas)技术应用演示
- Web 2.0 用户界面技术 让服务器部署的应用程序具有优于浏览器的用户体验
- 未来web浏览技术提前体验:10个会让你惊叹不已的HTML5画布(canvas)技术应用演示
- 翻译:WebAssembly简介:我们为什么要关心这个技术?
- 再次 WebAssembly 技术探讨
- paip.提升用户体验---高性能web解决 c++ mycp 技术.
- 用NodeJS开发云计算时代Web服务的真正技术难点
- 如何评论浏览器最新的 WebAssembly 字节码技术?
- 体验Web技术魅力:25个HTML5 应用实验
- Progressive Web App是一个利用现代浏览器的能力来达到类似APP的用户体验的技术——不就是chrome OS吗?
- JS调用C层接口(webAssembly技术,环境配置到代码实现)
- 用SVG技术实现基于Web的GIS
- WEB打印的相关技术分析
- 变革性的Java Web模板技术 -- fastm
- 升级 Microsoft SharePoint 产品和技术的 Web 部件程序集
- XML与Web数据挖掘技术
- Web页面技术综述(包括fastm)
- 面向Web的数据挖掘 技术分析
- WEB打印的相关技术分析