此处参考原文,稍有改动,本文根据知识共享署名4.0国际许可协议共享,BYTroland。本系列持续更新中,Github地址请查看这里。这是JavaScript工作原理的第6章。现在,我们将剖析WebAssembly的工作原理,最重要的是它与JavaScript在性能方面的比较:加载时间、执行速度、垃圾收集、内存使用、平台API访问、调试、多线程和可移植性。我们构建Web应用程序的方式正面临一场革命——这仅仅是个开始,我们对Web应用程序的思考方式正在发生变化。首先,了解WebAssemblyWebAssembly(也称为wasm)是一种用于开发Web应用程序的高效低级字节码。WASM允许您使用JavaScript以外的语言(例如C、C++、Rust等)编写可编译为(早期)WebAssembly的应用程序。生成的Web应用程序加载和运行速度极快。加载时间为了加载JavaScript,浏览器必须加载所有文本格式的js文件。浏览器将更快地加载WebAssembly,因为WebAssembly只会传输已编译的wasm文件。wasm是一种低级的类汇编语言,具有非常紧凑的二进制格式。执行速度今天Wasm的运行速度只比本地代码慢20%。无论如何,这是一个令人惊讶的结果。它是一种被编译到沙箱环境中并在大量约束下运行以确保没有安全漏洞或对其进行加固的格式。与真正的本地代码相比,执行速度的下降可以忽略不计。另外,未来会更快。更令人欣喜的是,它具有非常好的浏览器兼容性特性——所有主流浏览器引擎都支持WebAssembly,运行速度相差无几。要了解与JavaScript相比WebAssembly的执行速度有多快,您应该首先阅读之前的JavaScript引擎工作原理文章。让我们快速了解一下V8是如何工作的:
V8技术:惰性编译左边是JavaScript源代码,包括JavaScript函数。首先,源代码将字符串转换为便于解析的token,然后生成语法抽象树。句法抽象树是JavaScript程序逻辑的内存表示。图生成后,V8直接进入机器码阶段。您基本上是遍历树,生成机器代码并获得编译后的函数。这里没有任何真正的尝试来加速这个过程。现在,让我们看看V8流水线的下一阶段在做什么:
V8流水线设计现在我们有了TurboFan,它是V8的优化编译器之一。当JavaScript运行时,很多代码都在V8内部运行。TurboFan监控运行缓慢的代码、导致性能瓶颈的地方和热点(内存使用率过高的地方)以优化它们。它将上面监控的代码推送到后端,一个优化的即时编译器,它将CPU密集型函数转换为性能更好的代码。它解决了性能问题,但缺点是分析代码并确定哪些代码需要优化的过程也会消耗CPU资源。这也意味着更多的功耗,尤其是在移动设备中。然而,wasm并不需要上述所有步骤——它被插入到执行过程中,如下所示:
V8pipelinedesign+WASMwasm已经在编译阶段通过了代码优化。简而言之,也不需要解析。您已经优化了可以直接插入后端(即时编译器)并生成机器代码的二进制代码。编译器已经完成了前端所有的代码优化工作。这使得wasm的执行效率更高,因为编译过程中的许多步骤都被跳过了。内存模型
WebAssembly可信和不可信状态比如一个C++程序的内存被编译成WebAssembly,它是一块连续的内存,没有空洞。wasam中可用于提高代码安全性的特性之一是执行堆栈和线性内存隔离的概念。在C++程序中,你有一个动态内存区域,你从底层分配得到内存栈,然后从顶层得到内存来增加内存栈的大小。你可以获得一个指针并遍历堆栈内存来操作你不应该接触的变量。这是大多数可疑软件都可以利用的漏洞。WebAssembly使用完全不同的内存模型。执行栈与WebAssembly程序本身是隔离的,所以你不能从内部修改和改变变量值之类的东西。同样,函数使用整数偏移量而不是指针。函数指向一个间接函数表。之后,这些直接计算的数字进入模块中的函数。这就是它的工作原理,这样您就可以同时引入多个wasm模块,偏移所有索引,每个模块都可以正常工作。更多关于JavaScript内存模型和管理的文章可以在这里找到。内存垃圾收集您已经知道JavaScript内存管理由内存垃圾收集器处理。WebAssembly的情况有点不同。它支持手动操作内存的语言。您还可以将内存中的垃圾收集器构建到wasm模块中,但这是一项复杂的任务。目前,WebAssembly是围绕C++和RUST的使用场景设计的。由于wasm是一种非常低级的语言,这意味着只比汇编语言高一级的编程语言可以很容易地编译成WebAssembly。C可以使用malloc,C++可以使用智能指针,Rust使用完全不同的模式(完全不同的主题)。这些语言不使用内存垃圾收集器,因此它们不需要所有复杂的运行时东西来跟踪内存。WebAssembly非常适合这些语言。另外,这些语言并不是100%适用于监控DOM变化等复杂的JavaScript使用场景。用C++编写整个HTML程序毫无意义,因为C++不是为此而设计的。大多数时候,工程师使用C++或Rust来编写WebGL或高度优化的库(例如繁重的数学运算)。不过,未来WebAssembly会支持没有垃圾回收的语言。平台接口访问依赖于执行JavaScript的运行环境,通过JavaScript程序可以直接访问这些平台暴露的指定接口。例如,当你在浏览器中运行JavaScript时,Web应用程序可以调用一系列Web接口来控制浏览器/设备功能并访问DOM、CSSOM、WebGL、IndexedDB、WebAudioAPI等。但是,WebAssembly模块没有访问权限到任何平台界面。所有这些都必须由JavaScript协调。如果你想访问WebAssembly模块中的一些特定于平台的接口,你必须通过JavaScript调用它们。例如,如果您想使用console.log,则必须从JavaScript而不是C++代码调用它。并且这些JavaScript调用会招致一定的性能损失。事情不会一成不变。该规范将在未来为wasm提供一个访问特定平台的接口,这样你就不需要在你的程序中内置JavaScript。SourceMaps在缩小JavaScript代码时,您需要有一种正确的方法来调试它。这是源地图派上用场的地方。基本上,源映射是一种将合并/压缩文件映射到未构建状态的方法。在为生产构建代码以及缩小和合并JavaScript时,会生成源映射以保留原始文件信息。当您想查询生成的JavaScript代码中的特定代码行和列时,您可以在源映射中进行查找以获取代码的原始位置。WebAssembly目前不支持它,因为没有定义源映射的规范,但它最终会(可能很快)。当您在C++代码中设置断点时,您将看到C++代码而不是WebAssembly。至少,这是WebAssembly源映射的目标。多线程JavaScript是单线程的。如上一篇文章所述,有许多方法可以利用事件循环和使用异步编程。JavaScript也使用WebWorkers,但仅在极其特殊的情况下使用——一般来说,任何可能阻塞主UI线程的CPU密集型计算都可以卸载到WebWorkers以获得更好的性能。但是,WebWorker无法访问DOM。目前WebAssembly不支持多线程。然而,这很可能是WebAssembly将要实现的下一件事。Wasm将接近于实现原生线程(例如,C++风格的线程)。拥有真正的线程将在浏览器中开辟许多新机会。当然,还会增加滥用的可能性。可移植性JavaScript现在几乎可以在任何地方运行,从浏览器到服务器甚至嵌入式系统。WebAssembly专为安全性和可移植性而设计。就像JavaScript一样。它将在任何支持wasm的环境中运行(例如每个浏览器)。WebAssembly与早年Java使用小程序实现的可移植性目标相同。WebAssembly使用场景WebAssembly的初始版本主要是为了解决大量的计算密集型计算(比如处理数学问题)。最主流的使用场景是游戏——处理大量像素。您可以使用您熟悉的OpenGL绑定编写C++/Rust程序,并编译为wasm。之后,它在浏览器中工作。向下浏览(在FireOrphan中运行)-http://s3.amazonaws.com/mozil…。这在Unreal引擎(这是一个可用于开发虚拟现实的开发工具包)上运行。WebAssembly有意义(高性能)的另一种情况是在实现一些处理密集型库时。比如一些图形操作。如前所述,wasm可以有效降低移动设备的功耗(取决于引擎),因为大部分步骤在编译阶段就已经提前处理好了。将来,即使您没有编写编译它的代码,也可以直接使用WASM二进制文件。你可以在NPM上找到一些开始使用这项技术的项目。对于操作DOM和频繁使用平台接口的情况,使用JavaScript更合理,因为它不会产生额外的性能开销,而且它原生支持各种接口。在SessionStack,我们一直致力于提高JavaScript的性能以编写高质量和高效的代码。我们的解决方案必须具有闪电般的性能,因为我们不能影响用户程序的性能。一旦SessionStack集成到Web应用程序或网站的生产环境中,它将开始记录所有内容:所有DOM更改、用户交互、JavaScript异常、堆栈跟踪、失败的网络请求和调试数据。所有这些都发生在生产中,而不会影响产品的任何交互和性能。我们必须大量优化我们的代码并使其尽可能异步。我们不仅有图书馆,还有其他功能!当您在SessionStack中重播用户会话时,我们必须呈现问题发生时用户浏览器中发生的一切,并且我们必须重建整个状态以允许您在会话时间轴上来回跳转。为了实现这一点,我们大量使用异步操作,因为在JavaScript中没有更好的选择。有了WebAssembly,我们可以把大量的数据计算和渲染工作交给更合适的语言来处理,把数据采集和DOM操作交给JavaScript来处理。打开webassembly官网后,可以在头部突出看到兼容的浏览器。它们是火谷、Chrome、Safari和IEEdge。点击learnmore可以看到这是2017/2/28约定的浏览器预览版。现在作品已经开始进入实施阶段,相信在未来的某个时间点能够在生产环境中使用。官网介绍了asm.js,它是JavaScript的一个子集。另外,这里还有一个WebAssembly和JavaScript性能对比的测试站点。本系列持续更新中,Github地址请查看这里。