How Javascript works (Javascript工作原理) (六) WebAssembly 对比 JavaScript 及其使用场景
个人总结:
1.webassembly简介:WebAssembly是一种用于开发网络应用的高效,底层的字节码。允许在网络应用中使用除JavaScript的语言以外的语言(比如C,C++,Rust及其他)来编写应用程序,然后编译成(提早)WebAssembly。
这是 JavaScript 工作原理的第六章。
现在,我们将会剖析 WebAssembly 的工作原理,而最重要的是它和 JavaScript 在性能方面的比对:加载时间,执行速度,垃圾回收,内存使用,平台 API 访问,调试,多线程以及可移植性。
我们构建网页程序的方式正面临着改革-这只是个开始而我们对于网络应用的思考方式正在发生改变。
首先,认识下 WebAssembly 吧
WebAssembly(又称 wasm) 是一种用于开发网络应用的高效,底层的字节码。
WASM 让你在其中使用除 JavaScript 的语言以外的语言(比如 C, C++, Rust 及其它)来编写应用程序,然后编译成(提早) WebAssembly。
构建出来的网络应用加载和运行速度都会非常快。
加载时间
为了加载 JavaScript,浏览器必须加载所有文本格式的 js 文件。
浏览器会更加快速地加载 WebAssembly,因为 WebAssembly 只会传输已经编译好的 wasm 文件。而且 wasm 是底层的类汇编语言,具有非常紧凑的二进制格式。
执行速度
如今 Wasm 运行速度只比原生代码慢 20%。无论如何,这是一个令人惊喜的结果。它是这样的一种格式,会被编译进沙箱环境中且在大量的约束条件下运行以保证没有任何安全漏洞或者使之强化。和真正的原生代码比较,执行速度的下降微乎其微。另外,未来将会更加快速。
更让人高兴的是,它具备很好的浏览器兼容特性-所有主流浏览器引擎都支持 WebAssembly 且运行速度相关无几。
为了理解和 JavaScript 对比,WebAssembly 的执行速度有多快,你应该首先阅读之前的 JavaScript 引擎工作原理的文章。
让我们快速浏览下 V8 的运行机制:
V8 技术:懒编译
左边是 JavaScript 源码,包含 JavaScript 函数。首先,源码先把字符串转换为记号以便于解析,之后生成一个语法抽象树。
语法抽象树是 JavaScript 程序逻辑在内存中的图示。一旦生成图示,V8 直接进入到机器码阶段。你基本上是遍历树,生成机器码然后获得编译后的函数。这里没有任何真正的尝试来加速这一过程。
现在,让我们看一下下一阶段 V8 管道的工作内容:
V8 管道设计
现在,我们拥有 TurboFan ,它是 V8 的优化编译程序之一。当 JavaScript 运行的时候,大量的代码是在 V8 内部运行的。TurboFan 监视运行得慢的代码,引起性能瓶颈的地方及热点(内存使用过高的地方)以便优化它们。它把以上监视得到的代码推向后端即优化过的即时编译器,该编译器把消耗大量 CPU 资源的函数转换为性能更优的代码。
它解决了性能的问题,但是缺点即是分析代码及辨别哪些代码需要优化的过程也是会消耗 CPU 资源的。这也即意味着更多的耗电量,特别是在手机设备。
但是,wasm 并不需要以上的全部步骤-它如下所示插入到执行过程中:
V8 管道设计 + WASM
wasm 在编译阶段就已经通过了代码优化。总之,解析也不需要了。你拥有优化后的二进制代码可以直接插入到后端(即时编译器)并生成机器码。编译器在前端已经完成了所有的代码优化工作。
由于跳过了编译过程中的不少步骤,这使得 wasm 的执行更加高效。
内存模型
WebAssembly 可信和不可信状态
举个栗子,一个 C++ 的程序的内存被编译为 WebAssembly,它是整段连续的没有空洞的内存块。wasam 中有一个可以用来提升代码安全性的功能即执行堆栈和线性内存隔离的概念。在 C++ 程序中,你有一块动态内存区,你从其底部分配获得内存堆栈,然后从其顶部获得内存来增加内存堆栈的大小。你可以获得一个指针然后在堆栈内存中遍历以操作你不应该接触到的变量。
这是大多数可疑软件可以利用的漏洞。
WebAssembly 采用了完全不同的内存模型。执行堆栈和 WebAssembly 程序本身是隔离开来的,所以你无法从里面进行修改和改变诸如变量值的情形。同样地,函数使用整数偏移而不是指针。函数指向一个间接函数表。之后,这些直接的计算出的数字进入模块中的函数。它就是这样运行的,这样你就可以同时引入多个 wasm 模块,偏移所有索引且每个模块都运行良好。
更多关于 JavaScript 内存模型和管理的文章详见这里。
内存垃圾回收
你已经知晓 JavaScript 的内存管理是由内存垃圾回收器处理的。
WebAssembly 的情况有点不太一样。它支持手动操作内存的语言。你也可以在 wasm 模块中内置内存垃圾回收器,但这是一项复杂的任务。
目前,WebAssembly 是专门围绕 C++ 和 RUST 的使用场景设计的。由于 wasm 是非常底层的语言,这意味着只比汇编语言高一级的编程语言会容易被编译成 WebAssembly。C 语言可以使用 malloc,C++ 可以使用智能指针,Rust 使用完全不同的模式(一个完全不同的话题)。这些语言没有使用内存垃圾回收器,所以他们不需要所有复杂运行时的东西来追踪内存。WebAssembly 自然就很适合于这些语言。
另外,这些语言并不能够 100% 地应用于复杂的 JavaScript 使用场景比如监听 DOM 变化 。用 C++ 来写整个的 HTML 程序是毫无意义的因为 C++ 并不是为此而设计的。大多数情况下,工程师用使用 C++ 或 Rust 来编写 WebGL 或者高度优化的库(比如大量的数学运算)。
然而,将来 WebAssembly 将会支持不带内存垃圾回功能的的语言。
平台接口访问
依赖于执行 JavaScript 的运行时环境,可以通过 JavaScript 程序来直接访问这些平台所暴露出的指定接口。比如,当你在浏览器中运行 JavaScript,网络应用可以调用一系列的网页接口来控制浏览器/设备的功能且访问 DOM,CSSOM,WebGL,IndexedDB,Web Audio API 等等。
然而,WebAssembly 模块不能够访问任何平台的接口。所有的这一切都得由 JavaScript 来进行协调。如果你想在 WebAssembly 模块内访问一些指定平台的接口,你必须得通过 JavaScript 来进行调用。
举个栗子,如果你想要使用 console.log
,你就得通过JavaScript 而不是 C++ 代码来进行调用。而这些 JavaScript 调用会产生一定的性能损失。
情况不会一成不变的。规范将会为在未来为 wasm 提供访问指定平台的接口,这样你就可以不用在程序中内置 JavaScript。
源码映射
当压缩了 JavaScript 代码的时候,你需要有合适的方法来进行调试。
这时候源码映射就派上用场了。
大体上,源码映射就是把合并/压缩了的文件映射到未构建状态的一种方式。当为生产环境进行代码构建的时候,与压缩和合并 JavaScript 一起,会生成源码映射用来保存原始文件信息。当你想在生成的 JavaScript 代码中查询特定的行和列的代码的时候,可以在源码映射中进行查找以获得代码的原始位置。
由于没有规范定义源码映射,所以目前 WebAssembly 并不支持,但最终会有的(可能快了)。
当在 C++ 代码中设置了断点,就会看到 C++ 代码而不是 WebAssembly。至少,这是 WebAssembly 源码映射的目标吧。
多线程
JavaScript 是单线程的。有很多方法来利用事件循环和使用在之前的文章中有提到的异步编程。
JavaScript 也使用 Web Workers 但是只有在极其特殊的情况下-大体上,可以把任何可能阻塞 UI 主线程的密集的 CPU 计算移交给 Web Worker 执行以获得更好的性能。但是,Web Worker 不能够访问 DOM。
目前 WebAssembly 不支持多线程。但是,这有可能是接下来 WebAssembly 要实现的。Wasm 将会接近实现原生的线程(比如,C++ 风格的线程)。拥有真正的线程将会在浏览器中创造出很多新的机遇。并且当然,会增加滥用的可能性。
可移植性
现在 JavaScript 几乎可以运行于任意的地方,从浏览器到服务端甚至在嵌入式系统中。
WebAssembly 设计旨在安全性和可移植性。正如 JavaScript 那样。它将会在任何支持 wasm 的环境(比如每个浏览器)中运行。
WebAssembly 拥有和早年 Java 使用 Applets 来实现可移植性的同样的目标。
WebAssembly 使用场景
WebAssembly 的最初版本主要是为了解决大量计算密集型的计算的(比如处理数学问题)。最为主流的使用场景即游戏-处理大量的像素。
你可以使用你熟悉的 OpenGL 绑定来编写 C++/Rust 程序,然后编译成 wasm。之后,它就可以在浏览器中运行。
浏览下(在火孤中运行)-http://s3.amazonaws.com/mozilla-games/tmp/2017-02-21-SunTemple/SunTemple.html。这是运行于Unreal engine(这是一个可以用来开发虚拟现实的开发套件)中的。
另一个合理使用 WebAssembly (高性能)的情况即实现一些处理计算密集型的库。比如,一些图形操作。
正如之前所提到的,wasm 可以有效减少移动设备的电力损耗(依赖于引擎),这是由于大多数的步骤已经在编译阶段提前处理完成。
未来,你可以直接使用 WASM 二进制库即使你没有编写编译成它的代码。你可以在 NPM 上面找到一些开始使用这项技术的项目。
针对操作 DOM 和频繁使用平台接口的情况 ,使用 JavaScript 会更加合理,因为它不会产生额外的性能开销且它原生支持各种接口。
在 SessionStack 我们一直致力于持续提升 JavaScript 的性能以编写高质量和高效的代码。我们的解决方案必须拥有闪电般的性能因为我们不能够影响用户程序的性能。一旦把 SessionStack 整合进网络应用或网站的生产环境,它会开始记录所有的一切:所有的 DOM 变化,用户交互,JavaScript 异常,堆栈追踪,失败的网络请求和调试数据。所有的这一切都是在生产环境中产生且没有影响到产品的任何交互和性能。我们必须极大地优化我们的代码并且尽可能地让它异步执行。
我们不仅仅有库,还有其它功能!当你在 SessionStack 中重放用户会话,我们必须渲染问题产生时用户的浏览器所发生的一切,而且我们必须重构整个状态,允许你在会话时间线上来回跳转。为了使之成为可能,我们大量地使用异步操作,因为 JavaScript 中没有比这更好的替代选择了。
有了 WebAssembly,我们就可以把大量的数据计算和渲染的工作移交给更加合适的语言来进行处理而把数据收集和 DOM 操作交给 JavaScript 进行处理。
番外篇
打开 webassembly 官网就可以在头部醒目地看到显示它兼容的浏览器。分别是火孤,Chrome,Safari,IE Edge。点开 learn more 可以查看到这是于 2017/2/28 达成一致推出浏览器预览版。现在各项工作开始进入实施阶段了,相信在未来的某个时刻就可以在生产环境使用它了。官网上面介绍了一个 JavaScript 的子集 asm.js。另外,这里有一个 WebAssembly 和 JavaScript 进行性能比对的测试网站。
How Javascript works (Javascript工作原理) (六) WebAssembly 对比 JavaScript 及其使用场景的更多相关文章
- How Javascript works (Javascript工作原理) (八) WebAssembly 对比 JavaScript 及其使用场景
个人总结: webworker有以下三种: Dedicated Workers 由主进程实例化并且只能与之进行通信 Shared Workers 可以被运行在同源的所有进程访问(不同的浏览的选项卡,内 ...
- How Javascript works (Javascript工作原理) (七) WebAssembly 对比 JavaScript 及其使用场景
个人总结: 1.webworkers实现了用多线程浏览器来进行多线程操作js的能力. 2.web workers不能操作dom,window,document等对象,一般用于cpu计算型的任务. ...
- JavaScript 工作原理之六-WebAssembly 对比 JavaScript 及其使用场景
原文请查阅这里,略有改动,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland. 本系列持续更新中,Github 地址请查阅这里. 这是 JavaScript 工作原理的第六章. 现在 ...
- JavaScript定时器的工作原理(翻译)
JavaScript定时器的工作原理(翻译) 标签(空格分隔): JavaScript定时器 最近在看ajax原理的时候,看到了一篇国外的文章,讲解了JavaScript定时器的工作原理,帮助我很好的 ...
- [中英对照]How PCI Express Works | PCIe工作原理
How PCI Express Works | PCIe工作原理 PCI Express is a high-speed serial connection that operates more li ...
- [中英对照]How PCI Works | PCI工作原理
How PCI Works | PCI工作原理 Your computer's components work together through a bus. Learn about the PCI ...
- 我们应该如何去了解JavaScript引擎的工作原理
“读了你的几篇关于JS(变量对象.作用域.上下文.执行代码)的文章,我个人觉得有点抽象,难以深刻理解.我想请教下通过什么途径能够深入点的了解javascript解析引擎在执行代码前后是怎么工作的,ec ...
- 【转】我们应该如何去了解JavaScript引擎的工作原理
原文地址:http://www.nowamagic.net/librarys/veda/detail/1579 昨天收到一封来自深圳的一位前端童鞋的邮件,邮件内容如下(很抱歉,未经过他的允许,公开邮件 ...
- JavaScript原型继承工作原理
原型继承的定义 当你阅读关于JS原型继承的解释时,你时常会看到以下这段文字: 当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止.——出自JavaScript秘 ...
随机推荐
- C/C++中的函数指针
C/C++中的函数指针 一.引子 今天无聊刷了leetcode上的一道题,如下: Median is the middle value in an ordered integer list. If t ...
- 深入剖析 iOS 性能优化
问题种类 时间复杂度 在集合里数据量小的情况下时间复杂度对于性能的影响看起来微乎其微.但如果某个开发的功能是一个公共功能,无法预料调用者传入数据的量时,这个复杂度的优化显得非常重要了.上图列出了各种情 ...
- JS判断客户端是否是iOS或者Android或者ipad(一)
通过判断浏览器的userAgent,用正则来判断是否是ios和Android客户端.代码如下<script type="text/javascript"> var u ...
- easyui的增删改
陈旧的开发模式PM:“我要这个功能”后端:“这个先找前端做个模板”前端:“模板做完了”后端:“我来对接一下,这里样式不对”前端:“我改完了”后端:“功能交付”PM:“春节要加这个活动”后端:“这个先找 ...
- Eclipse配置SVN的几种方法及使用详情(此文章对Myeclipse同样适用)
一.在Eclipse里下载Subclipse插件 方法一:从Eclipse Marketplace里面下载 具体操作:打开Eclipse --> Help --> Eclipse Mark ...
- 使用LayUI在页面实现加载层(图标)代码:
实现代码: var index = layer.load({ shade: [0.4,'#def'], icon :' 实现效果: 可以使用 layer.close(index); 来控制其在什么时 ...
- npm run eject 命令后出现This git repository has untracked files or uncommitted changes错误
npm run eject 暴露隐藏的文件,不可逆 结果出现下面的问题 This git repository has untracked files or uncommitted changes: ...
- CentOS 7 安装配置MySQL
环境 CentOS Linux release 7.5.1804 (Core) MySQL:mysql80-community-release-el7-1 检查: 在centos7中默认的是maria ...
- sklearn学习6----交叉验证
1.kfold:自己分样本来交叉验证迭代 导入模块:from sklearn.model_selection import KFold 参数: KFold(n_splits=3, shuffle=Fa ...
- libvirtd.service
[root@kvm-server ~]# systemctl status libvirtd.service ● libvirtd.service - Virtualization daemon Lo ...