
本文基于我在2019年11月2日在莫斯科举行的ITSubbotnik会议上的演讲。
总的来说,我是一个后端程序员,但是我对此技术很感兴趣,它使我可以在前端使用我的后端知识。
问题
让我们从这种(相对较新的)技术解决的问题开始。 这个问题是在浏览器中快速执行代码 。 快速-意思是“比JavaScript更快”,理想情况下是我们当前处理器所允许的速度。
此外,从历史上看,围绕此问题逐渐出现了重要的附加要求:
- 零配置 -应该是“开箱即用”的解决方案,无非是浏览器。
- 安全 -新技术不应造成新的威胁,我们已经在安全性方面有所作为。
- 跨平台 -浏览器可在所有主要处理器(包括移动平台)上运行。
- 对开发人员来说很方便 -即对我们而言。
之前的历史
我们看到了许多在浏览器中执行代码的选项。 可以说,这个领域有赢家也有输家。

优胜者:这绝对是JavaScript; V8引擎使JS变得如此之快; 以及HTML5。
失败者:ActiveX-如果您还记得的话,这项技术使您可以对机器进行任何操作,即 安全性很差; Flash-现在我们正在见证Flash衰落的时代,尽管ActionScript内置在其中,实际上是相同的JavaScript; Silverlight-他出现得太晚了,无法占据一个重要的利基市场。
结果,所有插件丢失,包括由于安全问题。
浏览器中已经有解决该问题的其他尝试:
- NaCl-Google提出的本地客户端; 直接在浏览器中的本机代码,这当然适用于跨平台; Mozilla不支持该计划,因此仅在Chrome中实现。
- PNaCl-便携式本机客户端-LLVM IR的子集用作便携式代码; Mozilla也不受支持,同样只有Chrome。 结果,谷歌于2017年5月拒绝支持PNaCl。
asm.js
asm.js是来自Mozilla基金会的另一个有趣的计划,它使我们接近WebAssembly的主题。 它于2010年出现,并于2013年公开发布。
asm.js是JavaScript的子集,您可以使用Emscripten编译器从C和C ++编译代码。
由于这也是JavaScript,因此该代码将在任何浏览器中执行。 此外,长期以来,主要的现代浏览器早已能够快速识别asm.js并将其有效地编译为处理器的本机代码。 与直接从C / C ++获取的本地代码相比,从asm.js获取的代码仅慢1.5到2倍(50-67%)。

左侧是C / C ++中最简单的函数的代码,右侧是编译成asm.js后的显示方式。 首先,我们在'use asm'
看到'use asm'
行,这是一个标记,这清楚地说明了接下来是asm.js的代码。 在这段代码中,我们看到|0
形式的构造,这是一个零值的按位或运算。 根据规范,此操作的结果是一个32位带符号整数。 但是这种类型甚至不在JavaScript中。 然而,这种类型是由于这种操作而产生的。 它本质上是对给定类型的转换。
总的来说,asm.js是利用我们对浏览器引擎如何编译JS的知识来优化这项工作的。
什么是WebAssembly?

WebAssembly (或Wasm )是一种在浏览器,虚拟机中运行的二进制格式,并且是从高级语言进行编译的结果。
Wasm不是一种编程语言,就像Java字节码不是一种编程语言一样,而是编译结果和运行中的代码块。
有人非常聪明地说, Web程序集 (即“ Web的汇编程序”)的名称是完全错误的,因为它不是汇编程序(不是编程语言),并且与Web无关(因为它只是一个虚拟机)。
WebAssembly的创建者受到以下目标和限制的指导-请观看视频Essing Wasm into a适当的误称:Andreas Rossberg 。

实际上,它们归结为三点:跨平台,紧凑,速度。 但是还有一个重要的要求,那就是“可销售性”-主要浏览器的开发人员应该采取主动。 结果,可以“出售”,Google,Mozilla,Microsoft和Apple的代表参加了该规范的开发。
让我们看看Wasm就像一个虚拟机。

这是一个“虚构的处理器”,但是没有寄存器,所有事情都是通过堆栈完成的。 只有四种数据类型:两个整数,两个浮点型。 相对简单的一组操作-请参见规范和交互式表格 。

平面内存模型:为内存分配了一个块,其大小为64 KB的倍数。 这是代码,数据,常量,全局变量,堆栈变小,堆变大。 您可以根据需要使堆自动增长,而内存块可以扩展为64 KB的倍数。
不使用指针(这样做是为了确保安全),而是使用索引。 索引是32位的,因此它最多可寻址4 GB的内存。
所有WebAssembly内存均可从JavaScript完全访问,以进行读取和写入。

让我们看一下WebAssembly运行时模型。 始终会从JavaScript加载和调用Wasm。 而且,JS和Wasm在同一沙箱中工作,并且由同一引擎执行。
请注意,您也可以从Wasm调用JS。 它可以是带有传递参数并返回值的函数调用,也可以只是执行任意字符串作为JS代码。
尝试WebAssembly
为了开始使用WebAssembly,我建议使用WasmFiddle网站或WebAssembly Studio-这是一种简单直观的方法,可以自己了解Wasm的真正含义。

在这里,C / C ++中的源代码在左上方(在这种情况下,是用于计算斐波那契数的递归函数),在右上方是用于加载Wasm,实例化Wasm模块并从中调用fib()
函数的JS代码。 当我们按下Build按钮时,我们得到一个Wasm代码块(.wasm文件),我们可以随时将其扩展为文本表示形式(.wat文件)。
带有括号的文本表示形式实质上是抽象语法树(AST)。 好吧,实际上带有方括号,它类似于Lisp语言。 从理论上讲,我们可以将代码编辑为文本表示形式,然后再次将其折叠成二进制格式-这让人联想到汇编语言编程。 但是通常由于高级语言的编译,我们会得到二进制Wasm。
2017年:生产就绪
2017年11月,WebAssembly被宣布为“可用于生产环境”。 准备了Wasm所有主要部分的规范,并在所有主要的浏览器中发布了Wasm的实现。 因此,对于WebAssembly,我们发布了MVP-最低可行产品1.0版。
浏览器支持
在2017年底,发布了所有支持WebAssembly的主要浏览器版本:

唯一的例外是IE11,它没有支持,很可能会消失。 假定对于较旧的浏览器,将使用polyfill-将Wasm转换为asm.js的功能; 有这样的原型,但据我所知,这些项目都被放弃了,显然,社区对此不负责任。
现在,在所有已安装的浏览器中, 约有88%支持Wasm 。

语言支持
为了使您喜欢的语言在Wasm中进行编译,您需要编译器来提供此编译目标。 现在,有许多语言支持Wasm,而且每个月都有越来越多的语言支持。 参见appcypher / awesome-wasm-langs 。
- C / C ++-通过Emscripten ,非常好的支持。
- Rust-Wasm支持在很久以前就出现了,Wasm周围的生态系统很大程度上是建立在Rust的基础上的。
- Java –通过TeaVM,JWebAssembly和Bytecoder在实验级别上。
- Kotlin-Kotlin / Native通过LLVM后端提供支持-实验。
- 去吧
- C#-通过Blazor (单声道)以及将来在Uno Platform上使用 。
- TypeScript-通过AssemblyScript 。
不久前,有消息称LLVM现在支持Wasm作为编译目标。 这使编程语言的开发人员更容易在Wasm中编译更多语言。
用例
这可能是主要问题之一-“如何使用Wasm?”
WebAssembly已经在积极使用:
其他可能的情况:
- 渐进式Web应用程序(PWA)
- 由训练有素的神经网络识别
性能表现
Wasm性能是其主要销售因素之一,但实际上会发生什么呢?
与JavaScript相比,事实证明,平均而言,Wasm更快,但是在每种特殊情况下,您都需要进行JS / Wasm比较,因为结果可能会好很多倍,也好几倍。 它也可能高度取决于所使用的浏览器。
实际上,JS和Wasm的最高性能是相同的,因为它们最终都变成了本机处理器代码。 但是JS在性能上容易损失得多,而Wasm提供了一种更“均匀”的方法。
通常,Wasm在体积计算中表现良好。 如果内存操作很多,Wasm就会丢失。 好吧,实际应用程序中的主要问题是JS <-> Wasm互操作缓慢。 例如参见基准 。

2019年7月,科学文章“不是那么快:分析WebAssembly与Web的性能 本机代码 。 “ 作者实现了在WebAssembly下运行Linux控制台实用程序以运行基准测试的功能,并使用SPEC基准测试与与asm.js和本机代码上的相同测试相比的Wasm性能。
结果如下:
- 比JavaScript快30%(这些测试的平均值)
- Wasm比本地代码慢50%(在这些测试中平均)
本文的作者还分析了Wasm“变慢”的原因:
- 与本地代码相比,数据加载/保存操作大约要多一倍;
- 更多分支-由访问内存时需要进行其他检查引起;
- 过去的L1缓存遗漏了更多内容。
通常,实际上,性能还不错。 此外,该分析将使浏览器开发人员可以更快地提高Wasm。
将来,Wem不仅会由于浏览器中更好的优化而加速,而且还将由于新功能而加速,例如:对内存的块操作,对SIMD指令的支持,对线程的支持。
接下来会发生什么?
WebAssembly如何发展?
首先,Wasm的规范团队继续工作。 规范处于不同的阶段(阶段),这项工作有一定的“路线图” 。
特别是,在不久的将来,我们希望进一步看到这些功能:
- 非陷阱浮点到整数转换 -现在在某些情况下从浮点值转换为整数可能会导致异常; 对于程序员而言,这是完全出乎意料的,因此所有此类转换都必须进行包装,从而失去性能。 此功能解决了该问题。
- 多值 -从函数返回一个以上值的能力,创建返回一个以上值的新指令的能力-例如,除法和除法余数,或加法和进位位的结果。
- Reference Types是对anyref类型的介绍,意思是“链接到JS堆上的东西”,这是加快与JS交互的第一步。
其次,对于浏览器开发者而言,它们实现了这些规范,即 逐渐将新功能添加到Wasm,首先将其隐藏在设置中的“标志下方”,然后默认情况下启用。
例如,这是与WebAssembly相关的Chrome功能列表 。
对于Firefox,可以在此处找到类似的列表。
Web浏览器外部的程序集
如上所述,Wasm本质上没有连接到Web,它只是一个虚拟机。 这意味着它也可以在网络外部使用。
现在有几种在浏览器外部使用Wasm的方案:
- Node.js-Node.js基于支持Wasm的V8引擎。
- 一个单独的控制台应用程序,即应用程序代码在Wasm VM中执行-这种运行时的示例: wasmtime ,wasmer。
- Wasm VM用作其他语言的库-例如, wasmer允许您使用多种语言进行调用。
对于在浏览器外部工作的Wasm,不再需要沙箱限制,相反,需要访问系统功能-文件系统和文件,控制台I / O等。 这导致了WebAssembly系统接口(WASI)的创建,这是一种类似于POSIX的跨平台API规范。 参见WebAssembly / WASI和wasi.dev 。

下一步是创建一个程序包管理器-Wasm程序包管理器 (WAPM) -wapm.io。 在这里,您可以获取完成的.wasm文件,并在您的应用程序中使用它。 通常,我们谈论的是一些著名库的Wasm版本。 有些软件包带有WASI标记,这意味着它们只能在浏览器之外的场景中使用。
结论
因此,WebAssembly很有可能使用,它已经“生产就绪”已有两年了。
与类似的JavaScript代码相比,使用Wasm可能会提高一些速度,但是您应该始终检查是否可以提高速度。
编程语言对wasm的支持正在不断发展。
很好,最重要的是,WebAssembly在某种程度上“改变了Web的面貌”-为我们提供了可以在应用程序中实现的新使用场景。
参考文献
很棒的清单:
影片: