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获得所有性能提升。
WebAssembly演示
![](https://i-blog.csdnimg.cn/blog_migrate/e6c81063682fea2803a8f179a7d49234.png)
免费学习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.instantiate
比WebAssembly.instantiateStreaming
宽松。 后者说WASM模块必须具有MIME类型的application/wasm
。 使用TypeError
时会遇到此问题。 如果您通过CDN提供WASM模块,并且无法控制MIME类型,请使用WebAssembly.instantiate
。 WebAssembly.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