webassembly_WebAssembly:解决Web上的性能问题

webassembly

在现代JavaScript中,目标通常是找到各种方法来优化浏览器的性能。 有时,Web应用程序需要高性能并期望浏览器跟上潮流。

由于引擎对待语言的方式,传统JavaScript具有性能限制。 作为页面一部分呈现的一种已解释(甚至是JIT编译)的语言只能得到很多-即使是使用功能最强大的硬件也是如此。

WebAssembly是专为解决性能问题而设计的。 它可以克服传统JavaScript不能解决的瓶颈问题。 在WebAssembly中,无需解析和解释代码。 WebAssembly充分利用其字节码格式来为您提供与本机程序匹配的运行时速度。

用另一种方式思考:将传统JavaScript想象成一个可以将您带到任何地方的好工具。 相比之下,WebAssembly是一种能够实现接近自然速度的高性能解决方案。 现在,您可以使用这两个单独的编程工具。

对我来说,问题是:WebAssembly是否可以代替旧的传统JavaScript? 如果没有,那么值得学习WebAssembly的投资吗?

什么是WebAssembly?

WebAssembly是可以发送到浏览器的另一种代码。 它采用bytecode格式,这意味着在到达浏览器时已以低级汇编语言交付。 字节码不是要手工编写的,而是可以从任何编程语言(例如C ++或Rust)进行编译的。 然后,浏览器可以获取任何WebAssembly代码,将其作为本机代码加载,并实现高性能。

您可以将此WebAssembly字节码视为一个模块:浏览器可以获取,加载和执行该模块。 每个WebAssembly模块都具有导入和导出功能,这些功能的行为与JavaScript对象非常相似。 WebAssembly模块的行为与其他任何JavaScript代码都非常相似,只是它以接近本地的速度运行。 从程序员的角度来看,您可以像处理当前JavaScript对象一样使用WebAssembly模块。 这意味着您对JavaScript和Web的了解已经转移到WebAssembly编程中。

WebAssembly工具通常包含一个C ++编译器。 当前的开发中有许多工具,但是Emscripten已经达到成熟。 该工具将C ++代码编译为WebAssembly模块,并构建可在任何地方运行的符合标准的模块。 编译后的输出将具有WASM文件扩展名,以指示它是WebAssembly模块。

WebAssembly的一个优点是,在获取模块时,您具有所有相同的HTTP缓存头。 另外,您可以使用IndexedDB缓存WASM模块,也可以使用会话存储来缓存模块。 缓存策略围绕缓存获取API请求并通过保留本地副本来避免另一个请求。 由于WebAssembly模块为字节码格式,因此您可以将该模块视为字节数组并将其存储在本地。

现在我们知道WebAssembly是什么,它有哪些局限性?

已知局限性

JavaScript在与任何典型C ++程序不同的环境中运行。 因此,限制包括本机API在浏览器环境中可以执行的操作。

网络功能必须是异步且无阻塞的操作。 所有底层JavaScript网络功能在浏览器的Web API中都是异步的。 但是,WebAssembly不能从异步I / O绑定操作中受益。 I / O操作必须等待网络响应,这使得所有近乎原生的性能提升都可以忽略不计。

在浏览器中运行,在沙盒环境中运行并且无法访问文件系统的代码。 您可以创建一个内存中虚拟文件系统,而不是预先加载了数据。

该应用程序的主循环使用协作式多任务处理,每个事件都有执行的机会。 网络上的事件通常来自鼠标单击,手指轻击或拖放操作。 该事件必须将控制权返回给浏览器,以便可以处理其他事件。 避免劫持主事件循环是明智的,因为这可能会变成调试的噩梦。 DOM事件通常与UI更新相关,这很昂贵。 这给我们带来了另一个限制。

Web程序集无法访问DOM。 它依靠JavaScript函数进行任何更改。 当前,有一个建议允许与Web上的DOM对象进行互操作 。 如果您考虑一下,DOM重绘速度慢且昂贵。 DOM阻碍了人们从接近本机的性能中获得的所有收益。 一种解决方案是将DOM抽象为内存中的本地副本,以后可以通过JavaScript进行协调。

在WebAssembly中,一些好的建议是坚持执行速度非常快的内容。 使用该工具可最大程度地提高性能,同时避免陷阱。 将WebAssembly视为这个超高速系统,可以在没有任何阻止程序的情况下隔离地良好运行。

WebAssembly中的浏览器兼容性不佳,但现代浏览器除外。 IE中没有支持。 但是Edge 16+确实支持WebAssembly。 Firefox 52 +,Safari 11+和Chrome 57+等所有现代大型企业都支持WebAssembly。 一种想法是进行特征检测并在WebAssembly模块之间进行特征奇偶校验,并回退到JavaScript。 这样,您就不会破坏网络,现代浏览器将从WebAssembly获得所有性能提升。

我可以使用wasm吗? 来自caniuse.com的主要浏览器对wasm功能的支持数据。

WebAssembly演示

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

谈话足够多; 是时候进行一些演示了。 这次,我们将探讨WebAssembly中的导出和导入功能。 导出和导入功能是与WebAssembly互操作性的标志。 这些功能使程序员可以像其他任何JavaScript对象一样使用WebAssembly模块。

导出功能是您从WebAssembly模块获得的功能 。 模块加载后,您将在instance.exports找到导出功能。 对于此演示,我将导出一个add函数,该函数计算您作为参数传递的两个数字的和。 计算将以接近本机的WebAssembly代码执行。 在此演示中,导出函数将是纯JavaScript函数-意味着它是无状态且不可变的。

导入功能是您输入到WebAssembly模块中的功能。 这是一个普通的旧JavaScript对象,具有回调函数。 然后,该模块使用WebAssembly中的参数调用该函数。 我将导入一个简单的回调,该回调从WebAssembly接收一个参数。 该参数是一个常量,分配的值为42。然后,我将使用此值从JavaScript设置DOM:

<p>Add result: <span id="addResult"></span></p>
<p>Simple result: <span id="simpleResult"></span></p>

导出的WebAssembly函数

首先,让我们看一下WebAssembly模块的文本格式。 这是人类可以阅读的WASM模块的文本表示。 它是为文本编辑器或任何其他可以使用纯文本的工具而设计的:

(module
  (func $add (param $lhs i32) (param $rhs i32) (result i32)
    get_local $lhs
    get_local $rhs
    i32.add)
  (export "add" (func $add)))

在这里了解每个细节并不是太重要。 这是WebAssembly模块的文本格式,通常以WAT文件扩展名查找。 i32.add使用近似本机代码执行加法。 然后, export "add" func $add并将其提供给JavaScript。

要加载WebAssembly模块,您可以执行以下操作:

// URL to the WASM module
const WASM_ADD_MODULE = 'https://myhost.com/add.wasm';

fetch(WASM_ADD_MODULE)
  .then(response => response.arrayBuffer())
  .then(bytes => WebAssembly.instantiate(bytes))
  .then(result => document.getElementById('addResult').innerHTML =
    result.instance.exports.add(1, 5));

Fetch API从URL获取模块并将其转换为字节数组。 此字节数组来自response.arrayBuffer 。 请注意,检查导出的函数exports.add表示它已编译为本机代码:

function 0() {
  [native code]
}

一个陷阱是,使用WebAssembly.instantiateWebAssembly.instantiateStreaming宽松。 后者说WASM模块必须具有MIME类型的application/wasm 。 使用TypeError时会遇到此问题。 如果您通过CDN提供WASM模块,并且无法控制MIME类型,请使用WebAssembly.instantiateWebAssembly.instantiateStreaming比以前的效率更高,但是它是更新的Web API,因此尚不能在所有现代浏览器中使用。

导入的WebAssembly函数

对于导入的功能,请以文本格式从此模块开始。 想象一下在WebAssembly中进行这种CPU约束且昂贵的计算。 如此激烈,实际上,这是对生命和一切终极问题的答案。

例如:

(module
  (func $i (import "imports" "imported_func") (param i32))
  (func (export "exported_func")
    i32.const 42
    call $i))

请注意,声明了常数i32.const 42 。 然后,使用导入的函数并通过call $i调用回调函数。 export "exported_func"声明了从JavaScript调用的导出函数的名称。

在JavaScript中,我们可以通过以下方式使用此模块:

const WASM_SIMPLE_MODULE = 'https://myhost.com/simple.wasm';

const simpleFn = (arg) => document.getElementById('simpleResult').innerHTML = arg;
const importSimpleObj = {imports: {imported_func: simpleFn}};

fetch(WASM_SIMPLE_MODULE)
  .then(response => response.arrayBuffer())
  .then(bytes => WebAssembly.instantiate(bytes, importSimpleObj))
  .then(result => result.instance.exports.exported_func());

查看importSimpleObj ,因为这是具有回调函数JavaScript对象。 然后exports.exported_func执行WebAssembly模块。 调用后,导入的函数simpleFn将使用常量参数运行。

以下是您可以试用的CodePen演示。 随意检查此代码示例中的每个函数和对象。 这将给与WebAssembly集成所需的粘合代码带来良好的感觉。

请参阅CodePen上的SitePoint@SitePoint )提供的Pen WebAssembly演示

结论

为了回答我的第一个原始问题,WebAssembly是对Web的很好补充。 它并不是要替代JavaScript,而只是增强了当前的网络技术。 任何寻求速度,效率和高性能的Web工程师都应该考虑WebAssembly。 JavaScript充当执行和处理WebAssembly结果的粘合代码。

一种想法是移植现有JavaScript代码,这些代码可以完成很多CPU约束的工作,例如,仅抽象真实DOM的DOM内存虚拟表示。 例如,WebAssembly端口还可以为尚不支持WebAssembly的浏览器提供优雅的后备。

随着WebAssembly模块的普及,npm软件包可能随这些模块一起提供了很好JavaScript抽象。 这既增强了当前的生态系统,又增加了代码重用。 有时候可能不需要编写自己的WebAssembly模块。

WebAssembly带来了无限的可能性。 您现在可以将其添加到您的武器库中,以解决Web上遇到的许多性能瓶颈。

翻译自: https://www.sitepoint.com/webassembly-solving-performance-problems/

webassembly

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值