WebAssembly
简介
WebAssembly 是一种可以使用非 JavaScript 编程语言编写代码并且能在浏览器上运行的技术方案
编程中的两种代码翻译方法
- 解释器: 翻译的过程是一行一行及时生效的
- 编译器: 编译是在执行前进行翻译
解释器的利弊
解释器很快的获取代码并且执行。不需要在可以执行代码的时候知道全部的编译步骤。因此,解释器感觉与 JavaScript 有着自然的契合。web 开发者能够立即得到反馈很重要。
但是实用解释器的弊端是当你运行相同的代码的时候。比如,你执行了一个循环。然后你就会一遍又一遍的做同样的事情。
编译器的利弊
编译器则有相反的效果。在程序开始的时候,它可能需要稍微多一点的时间来了解整个编译的步骤。但是当运行一个循环的时候他会更快,因为他不需要重复的去翻译每一次循环里的代码。
不同的浏览器实现起来稍有不同,但是基本目的是相同的。他们给 JavaScript 引擎添加了一个新的部分(JIT),称为监视器(也称为分析器)。该监视器在 JavaScript 运行时监控代码,并记录代码片段运行的次数以及使用了那些数据类型, 如果相同的代码行运行了几次,这段代码被标记为 “warm”。如果运行次数比较多,就被标记为 “hot”。被标记为 “warm” 的代码被扔给基础编译器,只能提升一点点的速度。被标记为 “hot” 的代码被扔给优化编译器,速度提升的更多
JavaScript运行
Parsing ( 解析 ) --> 将源码转换成解释器可以运行的东西 ( AST ) 所做的事情。
Compiling + optimizing ( 编译和优化 ) --> 花费在基础编译和优化编译上的时间。有一些优化编译的工作不在主线程,所以这里并不包括这些时间。
Re-optimizing ( 重新优化 ) --> 当预先编译优化的代码不能被优化的情况下,JIT 将这些代码重新优化,如果不能重新优化就丢给基础编译去做
Execution ( 执行 ) --> 执行代码的过程
Garbage collection ( 垃圾回收 ) --> 清理内存的时间
注意:
这些任务不会发生在离散块或特定的序列中。相反,它们将被交叉执行。比如正在做一些代码解析时,还执行着一些其他的逻辑,有些代码编译完成后,引擎又做了一些解析,然后又执行了一些逻辑,等等。
WebAssembly运行
- JavaScript源码被解析为抽象语法树 ( AST ) 然后转换为V8引擎的中间层字节码
- Decode ( 解码 ) --> WebAssembly本身已经是字节码, 解码即可
- Compiling + optimizing ( 编译和优化 ) -->
- JavaScript 是在执行代码期间编译的。因为 JavaScript 是动态类型语言,相同的代码在多次执行中都有可能都因为代码里含有不同的类型数据被重新编译。这样会消耗时间
- WebAssembly 与机器代码更接近
- 编译器不需要在运行代码时花费时间去观察代码中的数据类型,在开始编译时做优化。
- 编译器不需要去每次执行相同代码中数据类型是否一样。
- 更多的优化在 LLVM 最前面就已经完成了。所以编译和优化的工作很少。
- Re-optimizing ( 重新优化 ) -->
- JIT 基于运行代码的假设不正确时会重新优化 如: 当进入循环的变量与先前的迭代不同时,或者在原型链中插入新函数时
- WebAssembly 中,类型是明确的,因此 JIT 不需要根据运行时收集的数据对类型进行假设。这意味着它不必经过重新优化的周期
- Execution ( 执行 ) -->
- 人们为了使他们的代码更易于阅读(例如:将常见任务抽象为跨类型工作的函数)会阻碍编译器优化代码
- 执行 WebAssembly 代码通常更快。有些必须对 JavaScript 做的优化不需要用在 WebAssembly 上
- 由于程序员不需要直接编程,WebAssembly 提供了一组更适合机器的指令。根据您的代码所做的工作,这些指令的运行速度可以在10%到800%之间
- Garbage collection ( 垃圾回收 ) -->
- JavaScript因不能控制垃圾回收时机,所以它可能在非常重要的时间去工作,从而影响性能
- WebAssembly不支持垃圾回收。内存是手动管理的(就像 C/C++)。虽然这些可能让开发者编程更困难,但它的确提升了性能
- 补充:
- WebAssembly 和别的汇编语言是有一些不同的。它是一个概念机上的机器语言,不是在一个真正存在的物理机上运行的机器语言
- WebAssembly 指令有时候被称为虚拟指令。它比 JavaScript 代码更快更直接的转换成机器代码,但它们不直接和特定硬件的特定机器代码对应
- WebAssembly模块和JS模块之间存在重大差异。目前,WebAssembly 中的函数只能使用 WebAssembly 类型(整数或浮点数)作为参数或返回值
- 如果要在 JavaScript 和 WebAssembly 之间传递一个字符串,需要将字符转换为等效的字符码。然后你需要将它写入内存数组。由于索引是整数,所以可以将索引传递给 WebAssembly 函数。因此,字符串的第一个字符的索引可以当作指针
总结
使用WebAssembly,可以更快地在 web 应用上运行代码。这里有 几个 WebAssembly 代码运行速度比 JavaScript 高效的原因。
文件加载 - WebAssembly 文件体积更小,所以下载速度更快。
解析 - 解码 WebAssembly 比解析 JavaScript 要快
编译和优化 - 编译和优化所需的时间较少,因为在将文件推送到服务器之前已经进行了更多优化,JavaScript 需要为动态类型多次编译代码
重新优化 - WebAssembly 代码不需要重新优化,因为编译器有足够的信息可以在第一次运行时获得正确的代码
执行 - 执行可以更快,WebAssembly 指令更接近机器码
垃圾回收 - 目前 WebAssembly 不直接支持垃圾回收,垃圾回收都是手动控制的,所以比自动垃圾回收效率更高