我们通过在浏览器中编译为WASM使我们的音乐可视化器更快73%

2021-04-03 08:29:52

Webamp.org的Visualiers,ButterChurn,现在使用WebAsseMbly(WASM)来实现更好的性能和改进的安全性。虽然大多数项目通过将预先存在的本机代码编译为WASM来使用WASM,但ButterChurn使用浏览器编译器来编译不受信任的用户提供的代码,以在运行时快速安全的WASM。本博客帖子详细信息我们为什么要完成这个项目,我们面临的挑战,我们发现的解决方案以及他们解锁的绩效和安全赢得了。

Wavamp的Visualizer是由ButterChurn提供的,开源JavaScript和WebGL实现的Winamp的臭名昭着的Milkdrop Music Visualizer。 MilkDrop的标志性视觉效果是.Milk“预设”的产品,其中包含HLSL着色器代码和代码编写鳗鱼,这是一种NULLSOFT所发明的语言。 MilkDrop使用此代码将音频信号转换为像素。

Butterchurn最初通过将HLSL着色器代码转换为WebGL代码并将EEL代码转换为JavaScript。然后将结果代码分发为.JSON文件,从中ButterChurn可以读取代码和eval()。

这对着色器代码进行了很好的罚款,并且在大多数情况下,JavaScript代码足以在没有丢弃框架的情况下运行的事实是令人欣赏的现代JavaScript解释器状态的遗嘱。

但是,转帐的JavaScript代码仍然代表着重要的性能瓶颈。它的工作,但运行的黄蜂可以很容易地消耗大部分可用的CPU,排水电池并导致计算机风扇进入过驱动。

虽然JavaScript能够在动画循环内的热门循环中运行多个数学组成的复杂函数,但它不是作业的理想工具。

在2019年底,我开始了解更多关于编译器的更多信息,并为我试图实施一个。在寻找一个用于新手的编译项目时,我击中了编译鳗鱼的想法。它最终是一个完美的契合。 EEL是一种非常简单的语言,只有一个数据类型:浮点数(无字符串,对象或数组)。这使其非常适合WASM,只有有数字数据类型。

因此,在一个竞标中要学习编译器的工作原理,我构建了eel-wasm - 在类型签字中编写的编译器(更稍后的更多),它将eel源代码转换为WASM模块的二进制表示。如果你很好奇,你可以在这个游乐场里尝试一下:https://eel.capt.dev/

除了让我有机会掌握一下手动学习编译器的方式,它最终结束了叶子的显着性能改善!

要了解编译转为WASM的性能影响,我们构建了由105个预设组成的基准。对于每个预设,我们渲染了七个300帧的试验,每个试验都会学习渲染每个帧的花费了多少时间。

这些时间包括评估eel,以及运行框架JavaScript和WebGL着色器。在绝对术语中,我们发现在JavaScript版本中,我们每帧评估EEL的平均花费4.17ms。通过编制EEL来到WASM,我们每帧都能够将其降低到2.32ms。

要了解整体加速,我们计算每个预设的百分比加速并平均这些值。通过使用新的WASM版本的度量渲染比JavaScript方法快〜72.6。

虽然EEL-WASM为Butterchurn提供了重要的性能,但胜利并不立即。 Jordan Berg,Butterchurn的作者初步尝试在Butterchurn中采用EEL-WALM,并显示了WASM版本或多或少的性能中性。但为什么?

每个预设由多个鳗鱼功能组成,其中一些函数每个动画帧运行多达千次。在每个呼叫到EEL代码中,ButterChurn需要读出先前调用的结果并重置在预期下一个呼叫时重置一些全局值。虽然函数本身正在运行得更快,但获得价值观 - 即使是数字 - 进入和退出WASM最终令人惊讶地昂贵,有效地取消了我们的表现胜利。

Jordan Berg和我来回开始如何减少边界过境点的数量,我们最终发现了一个有趣的解决方案:在单独的WASM模块中重写这些热环,可以使用编译的eel wasm模块共享webassembly.global对象。虽然JavaScript和WASM之间的边界横穿昂贵,但多个WASM模块可以共享对全局的访问,并且似乎基本上支付零开销。

结果是,ButterChurn现在包括自己的预编译WASM模块(编写的汇编)与我们编译的EEL代码共享全局。当我们在热环中调用我们的EEL功能时,这个新模块负责阅读/重置WASM Globals。

在将最明显的热环移动到大会中,我们能够达到上述72.6%的性能改进。此外,仍然存在包含边界交叉的几个热环。我们估计,一旦我们将那些转换为组装,我们将能够整体实现全面的100%性能改善。

我对此多种母语模块方法看涨,因为它不仅允许我们避免边界过境的成本,而是在未来它将允许我们迭代地将其他性能敏感的ButterChurn的JavaScript转换为WASM。

早些时候我答应解释为什么我在类型签字中写了编译器。这是因为我们希望能够在浏览器中运行编译器。这是一个不寻常的选择,所以我想解释我们的动机:

首先,MilkDrop具有内在的预设编辑器,可让用户通过直接在VisualIzer窗口中修改代码来创建和编辑预设。我们希望将来支持此功能,并将需要在本地编译/解释EEL代码。

其次,韦姆姆目前支持从&gt的收集加载任何预设; 40,000预设,乔丹伯格在互联网档案中积累了大使。我们希望能够在编译器上迭代,而无需每次为这些预设中的每一个重新生成伪像。

有趣的是,包括作为运行时的一部分的鳗鱼编译器也是克赖银行本身的含量。 MilkDrop具有多个内联__asm()块,其编译器在运行时将其粘在一起以构建用户的程序。

虽然我简要探索了rust并将其编写编译器(编译器)编写到WASM,但我决定了此方法,因为编译器更简单地写入TypeScript。此外,EEL程序非常小,因此编译器性能不如捆绑大小重要。这说,探索重写鳗鱼狼在锈病中可能很有趣!

说实话,我对这位编译器的原始动机不是表现。我持怀疑态度,黄斯特将大大出现大量执行数学运营的JIT优化的JavaScript。虽然我很高兴被证明是错误的,但我的原始动机是利用WASM沙箱。

以前,为了呈现预设,vibamp将需要获取预处理的.json文件,该文件包含作为字符串的eel代码的JavaScript表示。然后它需要eval()javascript。

这对于默认预设很好,但我们还支持通过查询参数加载预设:https://webamp.org/?buttrchurnpreseturl=https://example.com/preset.json。这允许Internet存档为其40K预设中的每一个生成链路,这将打开具有该预设的频率。但是,它也相当于XSS安全漏洞,其中有人可以创建引用包含可恶意JavaScript的预设文件的播放器链接,然后将在WebAMP.org的上下文中执行。这阻止了我们安全地启用Dropbox集成等功能,可以直接从Dropbox帐户中流流。

随着EEL-WALM的采用,我们仍然可以支持渲染任意预设,但不受信任的代码在WASM沙箱中执行,因此我们有强保证,它无法读取或写出任何未明确传递的数据。

我想给予Jordan Berg,Butterchurn作者的jordan berg,回答关于鳗鱼语言的无尽问题以及Whilddrop如何使用它以及为将鳗鱼族融为荆棘的努力工作。