分享免费的编程资源和教程

网站首页 > 技术教程 正文

使用 WebAssembly 将前端代码速度提升20%!

goqiw 2025-01-03 15:45:08 技术教程 14 ℃ 0 评论

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

WebAssembly那些事

无论使用的是 Chrome、Firefox、Edge 还是 Safari浏览器,代码都是由 JavaScript 引擎解释和执行的,即浏览器只运行 JavaScript。 不幸的是,JavaScript 并不总是开发者想要执行的每项任务的理想选择,这就有了 WebAssembly 的用武之地。下面是MDN上对WebAssembly的描述:

WebAssembly or "wasm" is a new portable, size- and load-time-efficient format suitable for compilation to the web.

Chrome>57、Edge>16、Safari>11、FireFox>52、Opera>44等浏览器都已经支持WebAssembly。WebAssembly 是一种可以在现代浏览器中运行的新型代码, 它的创建是为了能获得更好的性能优势。 它是一种更偏底层的二进制格式,体积小,因此加载和执行速度很快。 开发者也不用直接编写 WebAssembly,而是可以通过将其他高级语言编译成为WebAssembly。

WebAssembly(汇编)通常是指类似于机器代码的人类可读语言,机器代码是处理器所理解的,即一堆数字。

为了在处理器上运行,每种高级编程语言都会被翻译成机器代码。不同类型的处理器架构需要不同的机器代码和不同类型的汇编。

尽管名字叫 WebAssembly,但它并不完全是一种汇编语言,因为它并不适用于任何特定的机器。 它适用于浏览器,当开发者交付要在浏览器中执行的最终代码时,开发者并不知道代码将在哪种机器上运行,而WebAssembly可以做到开发者底层完全无感。

实际上,WebAssembly 是一种用于概念机器的语言, 当浏览器下载 WebAssembly 代码时,它可以快速将其转换为任何机器的程序集。比如:下图展示了 WebAssembly 的格式,它具有易于阅读的文本格式特性 (.wat),而右侧的二进制表示是实际交付给浏览器运行的内容 (.wasm)。

WebAssembly 能够让开发者将 C、C++ 或 Rust 代码之类的东西编译成所谓的 WebAssembly 模块,同时可以将其加载到 Web 应用程序中并从 JavaScript 中调用它。但是需要声明的是:WebAssembly不是 JavaScript 的直接替代品,它的设计理念是与JavaScript一起工作。

1.为什么说Web是终态

Web的优势在于它在任何地方都有效,避免下载和安装应用程序等繁琐流程,从而实现立即交付。 它比直接在计算机上下载和运行二进制文件更安全,因为浏览器建立了安全机制,可以防止其中运行的代码干扰系统。 同时,在 Web 上共享内容也非常容易 ,通过URL即可实现,开发者还可以将Web内容托管在任何地方。

Web是应用程序可在任何设备上访问、共享的唯一真正通用的平台,从而允许开发者维护一个单一的代码库,同时保持更新迅速。

2.为什么需要 WebAssembly

比如一些特定的场景:视频游戏、视频编辑、3D 渲染或音乐制作等, 这些应用程序需要进行大量计算并且需要很高的性能,这种性能很难通过JavaScript得到保障。

JavaScript 最初只是一种简单的脚本语言,旨在为充满轻量级超文本文档的网络带来一些交互性。 它被设计为易于学习和编写,但并不是为了速度而设计的。 多年来,浏览器对它们解释 JavaScript 的方式进行了优化,从而带来了重大的性能改进。

随着JavaScript变得越来越快,开发者可以在浏览器中执行的操作开始逐步扩展。 新的 API 带来了交互式图形、视频流、离线浏览等功能。 反过来,越来越多以前仅限于本机的丰富应用程序开始出现在网络上。 今天,用户可以轻松地从浏览器编辑文档和发送电子邮件,但在某些领域,JavaScript 的性能仍然是无法磨灭的硬伤。

比如:视频游戏的性能局限一直极具挑战性,因为它们不仅要协调音频和视频,而且通常还要协调物理和人工智能,而WebAssembly 可以解决这些问题。

3.WebAssembly优势

3.1 速度

下载执行速度

WebAssembly 专为速度而生,它的二进制文件比文本 JavaScript 文件小得多,从而下载速度飞快,特别是在低速网络场景。

WebAssembly的解码和执行速度也更快。 JavaScript 是一种动态类型语言,变量类型不必预先定义,也不需要预先编译。 这使得编写代码变得容易和快速,但这也意味着 JavaScript 引擎有更多的工作要做。 它必须在页面上执行时解析、编译和优化代码。

解析 JavaScript 涉及将纯文本转换为称为抽象语法树 (AST) 的数据结构,并将其转换为二进制格式。 WebAssembly 以二进制形式交付,解码速度更快。 它是静态类型的,因此与 JavaScript 不同,引擎不需要在编译期间推测将使用什么类型。

大多数优化发生在源代码编译期间,甚至在它进入浏览器之前。 内存是手动管理的,就像 C 和 C++ 等语言一样,因此也没有垃圾收集。 所有这些都提供了更好、更可靠的性能。据统计, WASM 二进制文件的执行时间仅比相同本机代码的执行时间慢 20%。

WebAssembly缓存机制

浏览器为WebAssembly提供了很好的优化缓存机制,最大限度的提升浏览器中WebAssembly的运行效率。比如下面使用流式API方法compileStreaming 或 instantiateStreaming 编译或实例化 WebAssembly 模块,优化WebAssembly 代码缓存。

(async () => {
  const fetchPromise = fetch('fibonacci.wasm');
  const { instance } = await WebAssembly.instantiateStreaming(fetchPromise);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

下面使用的是instantiateStreaming方法

(async () => {
  const response = await fetch('fibonacci.wasm');
  const module = await WebAssembly.compileStreaming(response);
  const instance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

注意:由于代码缓存取决于资源 URL 以及 .wasm 资源是否最新,开发人员应尽量保持两者稳定。 如果 .wasm 资源是从不同的 URL 获取,它被认为是不同,浏览器必须重新编译模块。 同样,如果 .wasm 资源在缓存中不再有效,则 Chrome 必须丢弃所有缓存的代码。

3.2 可移植性

要在设备上运行应用程序,它必须与设备的处理器架构和操作系统兼容。 这意味着需要为想要支持的每种操作系统和 CPU 架构组合编译源代码。 使用 WebAssembly 只需一个编译步骤,应用程序就可以在每一种现代浏览器中运行。

开发者不仅可以将自己的应用程序移植到 Web,还可以将现有的大量 C++ 库和开源应用程序移植到 Web。 它是一种几乎所有平台都支持的语言,包括 iOS 和 Android。 WebAssembly可以用作跨 Web 和移动部署的通用语言。

3.3 灵活性

到目前为止,JavaScript 一直是 Web 浏览器中唯一完全受支持的语言。 有了 WebAssembly,Web 开发人员将能够选择其他语言,更多的开发人员将能够为 Web 编写代码。 JavaScript 仍然是大多数用例的最佳选择,但当确实需要性能提升时,可以选择使用更加优秀的语言。 UI 和应用程序逻辑等部分用 JavaScript 编写,核心功能在 WebAssembly 中。


根据MDN数据文档数据,目前完全支持的语言是 C、C++ 和 Rust,但还有许多其他语言正在开发中,包括 Kotlin 和 .NET,这两种语言都已经提供了实验性支持。

4.使用WebAssembly

4.1 准备工作

如果您已经使用 Emscripten 等工具编译了另一种语言的模块,或者自己加载并运行代码,那么下一步是了解如何使用 WebAssembly JavaScript API 的其他功能。

Emscripten 是一个开源的编译器,可以将 C/C++ 代码编译为 WebAssembly 编程语言的代码。Emscripten 的底层是基于 LLVM 编译器的,可以查看其开源的 emscripten llvm 和 emscripten clang 。

让我们通过一步一步的例子来了解如何在 WebAssembly 中使用 Javascript API和如何在网页中加载一个 wasm 模块。

  1. 首先需要一个 wasm 模块,下载 simple.wasm 文件到本机的一个新的目录下。
  2. 确保本机使用的是支持 webassembly 的浏览器,Firefox 52+ 和 Chrome 57+ 是默认支持 webassembly 的。
  3. 然后,创建一个简单的 HTML 文件命名为 index.html 和并且你的本机的 wasm 文件处于同一目录下 ( 如果你没有模板可以使用我们提供的 simple template )
  4. 为了帮助理解发生了什么,让我们来看看这个 wasm 模块的文本表示 (也可以在将 WebAssembly 文本格式转换为 wasm见到):
(module
  (func $i (import "imports" "imported_func") (param i32))
  (func (export "exported_func")
    i32.const 42
        call $i))

在第二行,你将看到导入有一个两级命名空间 —— 内部函数 $i 是从 imports.imported_func 导入的。编写要导入到 wasm 模块的对象时,需要在 JavaScript 中反映这个两级命名空间。创建一个 <script></script> 节点在你的 HTML 文件中,并且添加下面的代码:

var importObject = {
  imports: {
      imported_func: function(arg) {
        console.log(arg);
      }
    }
  };

如上所述,在 imports.imported_func 中有我们导入的函数。

4.2 加载并使用 wasm 模块

当导入了对象后,将获取 wasm 文件,使其在 array buffer 可用,然后就可以使用其导出的函数。下面添加以下代码到你的脚本中:

fetch('simple.wasm')
.then(res =>
  res.arrayBuffer()
).then(bytes =>
  WebAssembly.instantiate(bytes, importObject)
).then(results => {
  results.instance.exports.exported_func();
});

这样做的结果是执行导出的 WebAssembly 函数 exported_func,又调用了另一个导入的 JavaScript 函数 imported_func, 它将 WebAssembly 实例中提供的值打印到控制台。

5.本文总结

本文主要和大家介绍WebAssembly,即浏览器支持的除JavaScript外的新的语言,文章聚焦在什么是WebAssembly、为什么说Web是终态 、为什么需要 WebAssembly 、WebAssembly优势 、如何使用WebAssembly等维度。因为篇幅有限,文章并没有过多展开,如果有兴趣,文末的参考资料提供了优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!


参考资料

https://developer.chrome.com/blog/wasm-debugging-2020/

https://developer.mozilla.org/zh-CN/docs/WebAssembly/Using_the_JavaScript_API

https://caniuse.com/?search=WebAssembly

https://www.codercto.com/a/59841.html

英文链接:https://blog.logrocket.com/webassembly-how-and-why-559b7f96cd71/

英文作者:Milica Mihajlija

翻译作者:高级前端进阶,有改动

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表