WebAssembly及其 API 的完整介绍

开发 后端
自从引入计算机以来,本地应用程序的性能有了巨大的提高。相比之下,web 应用程序相当慢,因为 JS 一开始并不是为了速度而构建的。但是由于浏览器之间的激烈竞争以及JS 引擎如V8的快速开发,使得 JS 能够在机器上快速运行。但是它仍然不能超过本机应用程序的性能。这主要是因为 JS 代码必须经历几个进程才能生成机器码。

[[350065]]

自从引入计算机以来,本地应用程序的性能有了巨大的提高。相比之下,web 应用程序相当慢,因为 JS 一开始并不是为了速度而构建的。但是由于浏览器之间的激烈竞争以及JS 引擎如V8的快速开发,使得 JS 能够在机器上快速运行。但是它仍然不能超过本机应用程序的性能。这主要是因为 JS 代码必须经历几个进程才能生成机器码。

随着 WebAssembly 的引入,现代 Web 发生革命性的变化,这项技术非常快。让我们看一下什么是 WebAssembly,以及如何与 JS 集成以构建快速的应用程序。

什么是 WebAssembly?

在了解 WebAssembly 之前,让我们看一下什么是 Assembly。

Assembly(汇编)是一种低级编程语言,它与体系结构的机器级指令有着非常密切的联系。换句话说,它只需一个进程就可以转换为机器可以理解的代码,即机器代码。此转换过程称为汇编。

WebAssembly可以简称为 Web 的汇编。它是一种类似于汇编语言的低级语言,具有紧凑的二进制格式,使您能够以类似本机的速度运行Web应用程序。它还为C,C ++和Rust等语言提供了编译目标,从而使客户端应用程序能够以接近本地的性能在Web上运行。

此外,WebAssembly 的出现是与 JS 一起运行,而不是取代 JS。使用 WebAssembly JavaScript API,你可以交替地运行来自任一种语言的代码,来回没有任何问题。这为我们提供了利用 WebAssembly 的强大功能和性能以及 JS 的通用性和适应性的应用程序。这为web应用程序打开了一个全新的世界,它可以运行最初并不打算用于web的代码和功能。

有什么区别

Lin Clark预测,2017年 WebAssembly 的引入可能会引发 web 开发生命中的一个新的拐点。早期的另一个拐点 生在引入 JITs 编译的时候,JIT 编译使JS 的速度提高了近10倍。

如果将 WebAssembly 的编译过程与 JS 的编译过程进行比较,会注意到几个过程已被剥离,其余过程已被修剪,如下所示:

JIT 是使 JavaScript 运行更快的一种手段,通过监视代码的运行状态,把 hot 代码(重复执行多次的代码)进行优化。通过这种方式,可以使 JavaScript 应用的性能提升很多倍。

仔细比较上图,注意到,重新参与WebAssembly已经完全被剥夺掉了。这主要是因为编译器不需要对WebAssembly代码做任何假设,因为诸如数据类型是在代码中明确提及。

但是 JS 不是这样的,因为JIT应该做一些假设来运行代码,如果假设失败,它需要重新优化它的代码。

如何获取 WebAssembly 代码

WebAssembly是一项伟大的技术,我们需要如何利用 WebAssembly 的强大功能呢?

有几种方法:

  • 不推荐从头编写 WebAssembly 代码,除非你非常了解基本知识
  • 从 C 编译为 WebAssembly
  • 从 C++ 编译为 WebAssembly
  • 从 Rust 编译为 WebAssembly
  • 使用 AssemblyScript 将 Typescript 编译为WebAssembly。对于不熟悉C/C ++或Rust 的 Web开发人员来说,这是一个不错的选择
  • 支持更多的语言选项。

此外,还有Emscripten和WebAssembly Studio之类的工具可以帮助您完成上述过程。

JS 的 WebAssembly API

为了充分利用 WebAssembly 的特性,我们必须将其与 JS 代码集成在一起,这可以在JavaScript WebAssembly API的帮助下完成。

模块编译和实例化

WebAssembly代 码驻留在.wasm文件中。这个文件应该被编译成特定于它所运行的机器的机器码。我们可以使用WebAssembly.compile方法来编译 WebAssembly 模块。

WebAssembly.instantiate方法实例化已编译模块。另外,我们也可以从.wasm文件获得的数组缓冲区传递到WebAssembly.instantiate方法中。这也适用,因为实例化方法有两个重载。

  1. let exports 
  2.  
  3. fetch('sample.wasm').then(response => 
  4.   response.arrayBuffer() 
  5. ).then(bytes => 
  6.   WebAssembly.instantiate(bytes) 
  7. ).then(results => { 
  8.   exports = results.instance.exports 
  9. }) 

上述方法的缺点之一是这些方法不能直接访问字节码,因此在编译/实例化wasm模块之前,需要采取额外的步骤将响应转换为ArrayBuffer。

相反,我们可以使用WebAssembly.compileStreaming / WebAssembly.instantiateStreaming方法来实现与上述相同的功能,其优点是可以直接访问字节码,而无需将响应转换为ArrayBuffer。

  1. let exports 
  2.  
  3. WebAssembly.instantiateStreaming(fetch('sample.wasm')) 
  4. .then(obj => { 
  5.   exports = obj.instance.exports 
  6. }) 

注意,WebAssembly.instantiate和WebAssembly.instantiateStreaming会返回实例以及已编译的模块,它们可用于快速启动模块的实例。

  1. let exports; 
  2. let compiledModule; 
  3.  
  4. WebAssembly.instantiateStreaming(fetch('sample.wasm')) 
  5. .then(obj => { 
  6.   exports = obj.instance.exports; 
  7.   //access compiled module 
  8.   compiledModule = obj.module; 
  9. }) 

导入对象

实例化 WebAssembly 模块实例时,可以选择传递一个导入对象,该对象将包含要导入到新创建的模块实例中的值,有 4 种类型:

  • global values
  • functions
  • memory
  • tables

可以将导入对象视为提供给模块实例的工具,以帮助它实现其任务。如果没有提供导入对象,编译器将分配默认值。

Global

WebAssembly.Global 对象表示一个全局变量实例, 可以被JavaScript 和importable/exportable 访问 ,跨越一个或多个WebAssembly.Module 实例. 他允许被多个modules动态连接.

可以使用WebAssembly.Global()构造函数创建全局实例。

  1. const global = new WebAssembly.Global({ 
  2.     value: 'i64'
  3.     mutable: true 
  4. }, 20) 

语法

  1. var myGlobal = new WebAssembly.Global(descriptor, value) 

global 构造函数接受两个参数。

descriptor

GlobalDescriptor 包含2个属性的表:

  • value: A USVString 表示全局变量的数据类型. 可以是i32, i64, f32, 或 f64
  • mutable: 布尔值决定是否可以修改. 默认是 false

value可以是任意变量值,需要其类型与变量类型匹配. 如果变量没有定义, 使用0代替

  1. const global = new WebAssembly.Global({ 
  2.     value: 'i64'
  3.     mutable: true 
  4. }, 20); 
  5.  
  6. let importObject = { 
  7.     js: { 
  8.         global 
  9.     } 
  10. }; 
  11.  
  12. WebAssembly.instantiateStreaming(fetch('global.wasm'), importObject) 

全局实例应该传递给importObject,以便在 WebAssembly 模块实例中可以访问它。

Memory

当 WebAssembly 模块被实例化时,它需要一个 memory 对象。你可以创建一个新的WebAssembly.Memory并传递该对象。如果没有创建 memory 对象,在模块实例化的时候将会自动创建,并且传递给实例。

JS引擎创建一个ArrayBuffer来做这件事情。ArrayBuffer 是 JS 引用的 JavaScript 对象。JS 为你分配内存。你告诉它需要多少内存,它会创建一个对应大小的ArrayBuffer

ArrayBuffer 做了两件事情,一件是做 WebAssembly 的内存,另外一件是做 JavaScript 的对象。

它使 JS 和 WebAssembly 之间传递内容更方便。

使内存管理更安全。

Table

WebAssembly.Table() 构造函数根据给定的大小和元素类型创建一个Table对象。

这是一个包装了WebAssemble Table 的Javascript包装对象,具有类数组结构,存储了多个函数引用。在 JS 或者WebAssemble中创建Table 对象可以同时被JS 或WebAssemble 访问和更改。

引入Table的主要原因是提高了安全性。我们可以使用set()、grow()和get()方法来操作表。

事例

为了演示,我将使用WebAssembly Studio应用程序将C文件编译为.wasm。

我已经在wasm文件中创建了一个函数来计算一个数字的幂。我将必要的值传递给函数,然后用JavaScript接收输出。

同样,我在wasm中进行了一些字符串操作。需要注意,wasm没有字符串类型。因此,它将使用ASCII值。返回到 JS 的值将指向存储输出的内存位置。由于内存对象是ArrayBuffer,因此我要进行迭代,直到收到字符串中的所有字符为止。

JavaScript文件

  1. let exports; 
  2. let buffer; 
  3. (async() => { 
  4.   let response = await fetch('../out/main.wasm'); 
  5.   let results = await WebAssembly.instantiate(await response.arrayBuffer()); 
  6.   //or 
  7.   // let results = await WebAssembly.instantiateStreaming(fetch('../out/main.wasm')); 
  8.   let instance = results.instance; 
  9.   exports = instance.exports; 
  10.   buffer = new Uint8Array(exports.memory.buffer); 
  11.  
  12.   findPower(5,3); 
  13.    
  14.   printHelloWorld(); 
  15.    
  16. })(); 
  17.  
  18. const findPower = (base = 0, power = 0) => { 
  19.   console.log(exports.power(base,power)); 
  20.  
  21. const printHelloWorld = () => { 
  22.   let pointer = exports.helloWorld(); 
  23.   let str = ""
  24.   for(let i = pointer;buffer[i];i++){ 
  25.     str += String.fromCharCode(buffer[i]); 
  26.   } 
  27.   console.log(str); 

C 文件

  1. #define WASM_EXPORT __attribute__((visibility("default"))) 
  2. #include <math.h> 
  3.  
  4.  
  5. WASM_EXPORT 
  6. double power(double number,double power_value) { 
  7.   return pow(number,power_value); 
  8.  
  9. WASM_EXPORT 
  10. char* helloWorld(){ 
  11.   return "hello world"

应用

WebAssembly 更适合用于写模块,承接各种复杂的计算,如图像处理、3D运算、语音识别、视音频编码解码这种工作,主体程序还是要用 javascript 来写的。

可以找我返现15(https://cn.aliyun.com/1111/home?userCode=pxuujn3r)

作者:Mahdhi Rezvi 译者:前端小智 来源:medium

原文:https://blog.bitsrc.io/a-complete-introduction-to-webassembly-and-its-javascript-api-3474a9845206 11111

菜v菜  本文转载自微信公众号「大迁世界」,可以通过以下二维码关注。转载本文请联系大迁世界公众号。

 

责任编辑:武晓燕 来源: 大迁世界
相关推荐

2023-12-10 16:48:00

Wasm浏览器

2017-03-19 20:41:57

WebAssemblyJavaScript编程

2021-12-27 10:46:07

WebAPIserver签名

2009-11-23 20:03:18

ibmdwLotus

2012-08-02 09:18:05

LESSSassCSS

2017-03-19 22:13:10

WebAssemblyJavaScript编程

2009-08-06 18:02:22

存储过程

2009-04-02 13:45:36

网关网络

2010-10-09 08:50:16

2009-06-19 18:42:06

jBPMSSH

2017-03-19 22:43:12

WebAssemblyJavaScript编程

2011-12-19 16:09:32

PhoneGap APCamera

2011-12-20 11:20:46

PhoneGap APCompass

2011-12-20 15:34:55

PhoneGap APConnection

2011-12-19 16:26:39

PhoneGap APCapture

2011-12-21 21:56:45

PhoneGap APFile

2011-12-22 09:27:36

PhoneGap APGeolocation

2011-12-20 13:32:52

PhoneGap APContacts

2011-12-22 10:33:39

PhoneGap APNotificatio

2011-12-22 10:45:32

PhoneGap APStorage
点赞
收藏

51CTO技术栈公众号