WebAssembly 助力 桌面端运行web

news2024/11/8 22:35:17

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

WebAssembly 是基于栈式虚拟机的二进制指令集,可以作为编程语言的编译目标,能够部署在 web 客户端和服务端的应用中。

第一次看到这个定义的时候是一头雾水,翻了一些资料渐渐有了点轮廓,下边分享下我目前的理解。

首先 WebAssembly 是由 WebAssembly 两个词构成,其中 Web 表明它一定和前端有关。Assembly 的意思是汇编,汇编对应机器码,而机器码和 CPU 的指令集有关,接下来补一下相关的知识。

相关概念

其中指令集、操作系统相关的知识,之前总结过几篇文章,到底学哪一门编程语言、x86,x64,x86-64,amd64,arm指令集架构之间的关系、linux和Android的关系,可以先过去看一下,这里的话抽主要的部分回顾一下。

参考上图,计算机的主要架构如上。最底层是 CPU 的指令集,主要分为复杂指令集和简单指令集。

复杂指令集是 x86x64(也叫 x86-64, amd64) 两种架构,专利在 IntelAMD 两家公司手里, 该架构 CPU 主要是 IntelAMD 两家公司,这种 CPU 常用在 PC 机上,包括 WindowsmacOSLinux

简单指令集是 arm 一种架构,专利在 ARM 公司手里,该架构 CPU 主要有高通、三星、苹果、华为海思、联发科等公司。这种 CPU 常用在手机上,包括安卓和苹果。

指令集是什么呢?直接把阮一峰的老师的一个 例子 粘过来,大家可以看一下。

c 语言的源程序。

int add_a_and_b(int a, int b) {
   return a + b;
}

int main() {
   return add_a_and_b(2, 3);
}

所对应的汇编就是下边的样子。

_add_a_and_b:
   push   %ebx
   mov    %eax, [%esp+8] 
   mov    %ebx, [%esp+12]
   add    %eax, %ebx 
   pop    %ebx 
   ret  

_main:
   push   3
   push   2
   call   _add_a_and_b 
   add    %esp, 8
   ret

这里的 pushmov 每一条指令就是指令集规定的内容,规定了操作码、操作数以及具体的功能。当然这里是用汇编表示的,主要是为了我们人类来读写,最终还会转成 0,1 序列。上边每个单词都会有一个数字相对应,比如 add 指令对应 00000011

通过规定的指令集(加法的指令,压栈指令等),编写相关程序,然后 CPU 就会一条一条的执行,最终实现相应的功能。

WebAssembly 就规定了一套指令集,更准确的来说是虚拟指令集,因为这套指令集是跑在虚拟机上的,而不是直接由硬件运行。

历史

上边我们知道了 WebAssemblyAssembly ,即汇编,也就是指令集。下边在回顾下 Web,即 WebAssembly诞生的原因。

这里就得谈到 javaScript 了,众所周知, javaScript 是一门动态类型的语言,编写程序时无需考虑变量类型,而且还可以运行时改变类型。对于我们开发者,确实很方便,但对于运行它的引擎就很有问题了。参考 这里 的一张图,看一下 V8 引擎从 js 源码到执行的一个过程。

由于 js 的动态类型,解释器在执行代码的时候会在类型判断上带来一定的性能消耗,降低执行速度。所以 V8 引擎采用了 JIT(即时编译技术) 技术,监控一些经常执行的代码,将其编译成 CPU 直接执行的机器码,提高执行速度。但由于 js 动态类型,在某些情况下还得反优化,回到字节码进行执行。

随着前端的不断发展,项目的大小和复杂度不断增大,对于某些场景,性能上可能已经无法满足,浏览器厂商们也一直在探索性能优化的方法。

NaCl/PNaCl

2011GoogleChrome 中使用了 NaCl 技术,可以使得 C 语言编写的程序运行到浏览器中,下边是维基百科 的定义。

Google Native Client(缩写为 NaCl),是一个由 谷歌所发起的 开放源代码计划,采用 BSD许可证。它采用 沙盒技术,让 Intel x86、 ARM或 MIPS子集的 机器代码直接在沙盒上运行。它能够从 浏览器直接运行程序机器代码,独立于用户的操作系统之外,使 Web应用程序可以用接近于机器代码运作的速度来运行,同时兼顾安全性。其功能类似于 微软的 ActiveX,但是ActiveX只支持视窗系统。

但一个完整的 NaCl 应用,在分发时需要提供支持多个架构平台(X86 / X64 / ARM 等)的模块文件,后来谷歌又推出了与底层架构无关的 PNaCl 技术。但由于其开发难度、兼容性等问题最终没有普及开来。在 2017Google 宣布放弃 PNaCl 转向 WebAssembly

ASM.js

ASM.jsMozilla2013 年推出的,是 javaScript 的一个严格子集,可以作为 C/C++ 编译的目标语言,从而使得 js 引擎可以采用 AOT(Ahead Of Time) 的编译策略,也就是在运行前直接编译成机器码,因此运行速度会有一定的提升。

ASM.js 通常不直接编写,而是作为一种通过编译器生成的中间语言,该编译器获取 C++ 或其他语言的源代码,然后输出 ASM.js

例如下边的 C 语言代码。

int f(int i) {
  return i + 1;
}

经过编译器编译会生成下边的 js 代码。

function f(i) {
  i = i|0;
  return (i + 1)|0;
}

注意这里的|0js 中相当于和 0 进行了或操作,所以不影响原本的逻辑。在 asm.js 中起到了类型标记的作用,这样 js 引擎执行的时候就知道 i 是一个整型,返回值是一个整型。除了或操作这种,ASM.js 标准中还规定了很多类似的标记规则,用于告诉 js 引擎变量的类型,便于进行 AOT 优化。

这看起来和 TypeScript 很像,但其实不是一种东西。TypeScriptjs 的一个超集,浏览器并不能直接执行 ts,还需要转换为 js 去执行。ts 主要是帮助我们开发人员去看的,增加了代码的可读性,也可以让编辑器提前发现一些错误。而 asm.js 是用于引擎的编译优化。

WebAssembly

接下来看一下 WebAssembly 的历史。

2015 年 4 月,WebAssembly Community Group 成立;
2015 年 6 月,WebAssembly 第一次以 WCG 的官方名义向外界公布;
2016 年 8 月,WebAssembly 开始进入了漫长的 “Browser Preview” 阶段;
2017 年 2 月,WebAssembly 官方 LOGO 在 Github 上的众多讨论中被最终确定;同年同月,一个历史性的阶段,四大浏览器(FireFox、Chrome、Edge、WebKit)在 WebAssembly 的 MVP(最小可用版本)标准实现上达成共识,这意味着 WebAssembly 在其 MVP 标准上的 “Brower Preview” 阶段已经结束;
2017 年 8 月,W3C WebAssembly Working Group 成立,意味着 WebAssembly 正式成为 W3C 众多技术标准中的一员。

WebAssembly2019125 日成为万维网联盟(W3C)的推荐标准,与 HTMLCSSJavaScript 一起成为 Web 的第四种语言。

可以看一下目前浏览器的支持程度,已经算比较高了。

初体验

内部结构

目前已经有了将 C/C++RusttsC#GoKotlinSwift 等语言转换为 WebAssembly(wasm) 的工具,下边我们体验一下 C++ 转换的过程。

首先编写一个 C++ 程序 fibonacci.cc,斐波纳契数字的递归写法。

#include <emscripten.h>
extern "C" {
  EMSCRIPTEN_KEEPALIVE 
  int fibonacci(int n) {
    if(n < 2) {
      return 1;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
  }
}

函数的定义置在 extern “C” {} 结构中,是为了防止函数名编译后被改变。EMSCRIPTEN_KEEPALIVE 是为了确保函数不会在编译器的编译过程中,被 DCE(Dead Code 」limination)过程处理掉。

然后需要安装 Emscripten 用来将 C++ 程序编译为 WebAssembly(wasm) 的程序,安装后执行下边的命令。

emcc fibonacci.cc -s WASM=1 -O3 --no-entry -o fibonacci.wasm

-s WASM=1 表明编译成 Webassembly 的程序,-O3 表明编译的优化程度,–no-entry 参数告诉编译器没有声明 main 函数,-o 指定生成的文件名。

让我们看一下生成的字节码文件 fibonacci.wasm

Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F   
00000000: 00 61 73 6D 01 00 00 00 01 11 04 60 00 01 7F 60    .asm.......`...`
00000010: 01 7F 01 7F 60 00 00 60 01 7F 00 03 07 06 02 01    ....`..`........
00000020: 00 03 01 00 04 05 01 70 01 02 02 05 06 01 01 80    .......p........
00000030: 02 80 02 06 0F 02 7F 01 41 90 88 C0 02 0B 7F 00    ........A..@....
00000040: 41 84 08 0B 07 88 01 09 06 6D 65 6D 6F 72 79 02    A........memory.
00000050: 00 19 5F 5F 69 6E 64 69 72 65 63 74 5F 66 75 6E    ..__indirect_fun
00000060: 63 74 69 6F 6E 5F 74 61 62 6C 65 01 00 09 66 69    ction_table...fi
00000070: 62 6F 6E 61 63 63 69 00 01 0B 5F 69 6E 69 74 69    bonacci..._initi
00000080: 61 6C 69 7A 65 00 00 10 5F 5F 65 72 72 6E 6F 5F    alize...__errno_
00000090: 6C 6F 63 61 74 69 6F 6E 00 05 09 73 74 61 63 6B    location...stack
000000a0: 53 61 76 65 00 02 0C 73 74 61 63 6B 52 65 73 74    Save...stackRest
000000b0: 6F 72 65 00 03 0A 73 74 61 63 6B 41 6C 6C 6F 63    ore...stackAlloc
000000c0: 00 04 0A 5F 5F 64 61 74 61 5F 65 6E 64 03 01 09    ...__data_end...
000000d0: 07 01 00 41 01 0B 01 00 0A 66 06 03 00 01 0B 3D    ...A.....f.....=
000000e0: 01 02 7F 41 01 21 01 20 00 41 02 4E 04 7F 41 00    ...A.!...A.N..A.
000000f0: 21 01 03 40 20 00 41 7F 6A 10 01 20 01 6A 21 01    !..@..A.j....j!.
00000100: 20 00 41 03 4A 21 02 20 00 41 7E 6A 21 00 20 02    ..A.J!...A~j!...
00000110: 0D 00 0B 20 01 41 01 6A 05 41 01 0B 0B 04 00 23    .....A.j.A.....#
00000120: 00 0B 06 00 20 00 24 00 0B 10 00 23 00 20 00 6B    ......$....#...k
00000130: 41 70 71 22 00 24 00 20 00 0B 05 00 41 80 08 0B    Apq".$......A...

让我们来解读下,最开始的前八个字节 0x0 0x61 0x73 0x6d 0x1 0x0 0x0 0x0 表明当前是一个 wasm 的模块。然后会分很多 SectionFunction SectionCode Section 等等,都有特定的数字对应,还有就是文章开头讲的指令操作符所对应的一些数字。

看着上边的字节码仿佛回到了上古时期直接用机器码编程的时代,当年出现了汇编语言。这里也会有类似汇编的东西,那就是 WAT(WebAssembly Text Format)

需要安装 WABT , 然后执行 wasm2wat 命令。

../wabt/bin/wasm2wat fibonacci.wasm -o fibonacci.wat

然后就生成了 fibonacci.wat 文件。

(module
  (type (;0;) (func (result i32)))
  (type (;1;) (func (param i32) (result i32)))
  (type (;2;) (func))
  (type (;3;) (func (param i32)))
  (func (;0;) (type 2)
    nop)
  (func (;1;) (type 1) (param i32) (result i32)
    (local i32 i32)
    i32.const 1
    local.set 1
    local.get 0
    i32.const 2
    i32.ge_s
    if (result i32)  ;; label = @1
      i32.const 0
      local.set 1
      loop  ;; label = @2
        local.get 0
        i32.const -1
        i32.add
        call 1
        local.get 1
        i32.add
        local.set 1
        local.get 0
        i32.const 3
        i32.gt_s
        local.set 2
        local.get 0
        i32.const -2
        i32.add
        local.set 0
        local.get 2
        br_if 0 (;@2;)
      end
      local.get 1
      i32.const 1
      i32.add
    else
      i32.const 1
    end)
  (func (;2;) (type 0) (result i32)
    global.get 0)
  (func (;3;) (type 3) (param i32)
    local.get 0
    global.set 0)
  (func (;4;) (type 1) (param i32) (result i32)
    global.get 0
    local.get 0
    i32.sub
    i32.const -16
    i32.and
    local.tee 0
    global.set 0
    local.get 0)
  (func (;5;) (type 0) (result i32)
    i32.const 1024)
  (table (;0;) 2 2 funcref)
  (memory (;0;) 256 256)
  (global (;0;) (mut i32) (i32.const 5243920))
  (global (;1;) i32 (i32.const 1028))
  (export "memory" (memory 0))
  (export "__indirect_function_table" (table 0))
  (export "fibonacci" (func 1))
  (export "_initialize" (func 0))
  (export "__errno_location" (func 5))
  (export "stackSave" (func 2))
  (export "stackRestore" (func 3))
  (export "stackAlloc" (func 4))
  (export "__data_end" (global 1))
  (elem (;0;) (i32.const 1) func 0))

上边的格式属于 「S- 表达式」, Lisp 语言就是采用的这种表达式,每条语句都是先执行最里边括号的表达式然后依次展开。

使用方法

上边主要介绍了 .wasm 具体长什么样子,下边看一下怎么用到浏览器中。

.wasm 源文件到实例化的对象主要有三个步骤,加载 -> 编译 -> 实例化 -> 调用

加载:读取 .wasm 字节码到本地中,一般是通过 fetch 从网络中取得。

编译:在 Worker 线程进行,编译成平台相关的代码。

实例化:将宿主环境的一些对象、方法导入到 wasm 模块中,比如导入操作 dom 的方法。

调用:通过上一步已经实例化的对象,来调用 wasm 模块中的方法。

主要有两种类型的 API,一种是 js 提供的 api ,另一种是 Web 提供的 apiWeb 提供的 api 支持流式编译实例化。

js 的方法,WebAssembly.instantiate(bufferSource, importObject),可以完成编译和实例化。

bufferSource 是含有效 Wasm 模块二进制字节码的 ArrayBufferTypedArray 对象。

importObject 是要导入到 Wasm 模块中的对象。

方法在调用后返回一个Promise 对象,resolve 后返回一个对象,该对象包含编译好的 module 和已经实例化的 instance,模块导出的方法可以通过 instance 对象进行调用。

web 的方法,WebAssembly.instantiateStreaming(source, importObject)

不同之处在于第一个参数,这里的 source 指的是尚未 ResolveResponse 对象(window.fetch 调用后会返回该对象),好处就是可以边读取 .wasm 字节流,边进行编译。

其他参数和返回值和 jsapi 均一致。

js API 尝试

先简单的尝试一下,我们直接构造一个 wasm 模块的 TypedArray 对象,该模块包含了一个 add 方法,然后调用 WebAssembly.instantiate 进行编译和实例化。

对应的 C++ 代码。

#include <emscripten.h>

extern "C" {
  EMSCRIPTEN_KEEPALIVE 
  int add(int a, int b) {
    return a + b;
  }
}

对应的 .wasm 字节码。

00 61 73 6D 01 00 00 00 01 17 05 60 00 01 7F 60
00 00 60 01 7F 00 60 01 7F 01 7F 60 02 7F 7F 01
7F 03 07 06 01 04 00 02 03 00 04 05 01 70 01 02
02 05 06 01 01 80 02 80 02 06 0F 02 7F 01 41 90
88 C0 02 0B 7F 00 41 84 08 0B 07 82 01 09 06 6D
65 6D 6F 72 79 02 00 19 5F 5F 69 6E 64 69 72 65
63 74 5F 66 75 6E 63 74 69 6F 6E 5F 74 61 62 6C
65 01 00 03 61 64 64 00 01 0B 5F 69 6E 69 74 69
61 6C 69 7A 65 00 00 10 5F 5F 65 72 72 6E 6F 5F
6C 6F 63 61 74 69 6F 6E 00 05 09 73 74 61 63 6B
53 61 76 65 00 02 0C 73 74 61 63 6B 52 65 73 74
6F 72 65 00 03 0A 73 74 61 63 6B 41 6C 6C 6F 63
00 04 0A 5F 5F 64 61 74 61 5F 65 6E 64 03 01 09
07 01 00 41 01 0B 01 00 0A 30 06 03 00 01 0B 07
00 20 00 20 01 6A 0B 04 00 23 00 0B 06 00 20 00
24 00 0B 10 00 23 00 20 00 6B 41 70 71 22 00 24
00 20 00 0B 05 00 41 80 08 0B

然后直接在控制台输入下边的代码。

WebAssembly.instantiate(new Uint8Array(`
  00 61 73 6D 01 00 00 00 01 17 05 60 00 01 7F 60
00 00 60 01 7F 00 60 01 7F 01 7F 60 02 7F 7F 01
7F 03 07 06 01 04 00 02 03 00 04 05 01 70 01 02
02 05 06 01 01 80 02 80 02 06 0F 02 7F 01 41 90
88 C0 02 0B 7F 00 41 84 08 0B 07 82 01 09 06 6D
65 6D 6F 72 79 02 00 19 5F 5F 69 6E 64 69 72 65
63 74 5F 66 75 6E 63 74 69 6F 6E 5F 74 61 62 6C
65 01 00 03 61 64 64 00 01 0B 5F 69 6E 69 74 69
61 6C 69 7A 65 00 00 10 5F 5F 65 72 72 6E 6F 5F
6C 6F 63 61 74 69 6F 6E 00 05 09 73 74 61 63 6B
53 61 76 65 00 02 0C 73 74 61 63 6B 52 65 73 74
6F 72 65 00 03 0A 73 74 61 63 6B 41 6C 6C 6F 63
00 04 0A 5F 5F 64 61 74 61 5F 65 6E 64 03 01 09
07 01 00 41 01 0B 01 00 0A 30 06 03 00 01 0B 07
00 20 00 20 01 6A 0B 04 00 23 00 0B 06 00 20 00
24 00 0B 10 00 23 00 20 00 6B 41 70 71 22 00 24
00 20 00 0B 05 00 41 80 08 0B`.trim().split(/[\s\r\n]+/g).map(str => parseInt(str, 16))
)).then(({instance}) => {
  const { add } = instance.exports
  console.log('2 + 4 =', add(2, 4))
})

然后就会看到输出了 2 + 4 = 6

Web API 尝试

我们再尝试一下流式编译。直接使用之前的斐波纳契数字的 fibonacci.wasm 模块。

首先我们需要提供一个简单的 HTTP 服务,用来返回 .wasm 文件。

新建一个 node.js 文件。

const http = require('http');
const url = require('url');
const fs = require('fs');
const path =require('path');

const PORT = 8888;  // 服务器监听的端口号;

const mime = {
  "html": "text/html;charset=UTF-8",
  "wasm": "application/wasm"  // 当遇到对 ".wasm" 格式文件的请求时,返回特定的 MIME 头;
};

http.createServer((req, res) => {
  let realPath = path.join(__dirname, `.${url.parse(req.url).pathname}`);
  // 检查所访问文件是否存在,且是否可读;
  fs.access(realPath, fs.constants.R_OK, err => {  
    if (err) {
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.end();
    } else {
      fs.readFile(realPath, "binary", (err, file) => {
        if (err) {
          // 文件读取失败时返回 500;          
          res.writeHead(500, { 'Content-Type': 'text/plain' });
          res.end();
        } else {
          // 根据请求的文件返回相应的文件内容;
          let ext = path.extname(realPath);
          ext = ext ? ext.slice(1) : 'unknown';
          let contentType = mime[ext] || "text/plain";
          res.writeHead(200, { 'Content-Type': contentType });
          res.write(file, "binary");
          res.end();
        }
      });
    }
  });
}).listen(PORT);
console.log("Server is runing at port: " + PORT + ".");

然后来编写我们的 html 文件,讲到斐波那契数字,我们顺便做一个性能的测试,来比较一下使用 wasm 的方式和原生 js 的求解速度。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>斐波纳切数字</title>
  </head>
  <script>
    function fibonacciJS(n) {
      if (n < 2) {
        return 1;
      }
      return fibonacciJS(n - 1) + fibonacciJS(n - 2);
    }
    const response = fetch("fibonacci.wasm");
    const num = [5, 15, 25, 35, 45];
    WebAssembly.instantiateStreaming(response).then(
      ({ instance }) => {
        let { fibonacci } = instance.exports;
        for(let n of num) {
          console.log(`斐波纳切数字: ${n},运行 10 次`)
          let cTime = 0;
          let jsTime = 0;
          for(let time = 0; time < 10; time++) {
            let start = performance.now();
            fibonacci(n)
            cTime += (performance.now() - start)

            start = performance.now();
            fibonacciJS(n)
            jsTime += (performance.now() - start)
          }
          console.log(`wasm 模块平均调用时间:${cTime / 10}ms`)
          console.log(`js 模块平均调用时间:${jsTime / 10}ms`)
        }

      }
    )
  </script>
  <body>
  </body>
</html>

然后执行 node node.js 开启 http 服务,接着在浏览器中打开 http://localhost:8888/index.html,控制台中输出如下:

斐波纳切数字: 5,运行 10 次
index.html:34 wasm 模块平均调用时间:0.001499993959441781ms
index.html:35 js 模块平均调用时间:0.005500001134350896ms
index.html:22 斐波纳切数字: 15,运行 10 次
index.html:34 wasm 模块平均调用时间:0.005999993300065398ms
index.html:35 js 模块平均调用时间:0.15650001005269587ms
index.html:22 斐波纳切数字: 25,运行 10 次
index.html:34 wasm 模块平均调用时间:0.6239999900572002ms
index.html:35 js 模块平均调用时间:1.1620000121183693ms
index.html:22 斐波纳切数字: 35,运行 10 次
index.html:34 wasm 模块平均调用时间:70.59700000681914ms
index.html:35 js 模块平均调用时间:126.21099999523722ms
index.html:22 斐波纳切数字: 45,运行 10 次
index.html:34 wasm 模块平均调用时间:8129.7520000021905ms
index.html:35 js 模块平均调用时间:16918.658500007587ms

整理成表格看一下:

可以看到 wasm 很明显的提高了运行速度,运行时间稳定在 js 的一半,当规模达到 45 的时候,wasm 的运行时间比 js 少了整整 8 秒。

这里也可以看出,如果对于计算密集型的应用,wasm 可以大展身手了。

前端应用

来看一些目前已经成功落地的 WebAssembly 的应用。

  1. eBay 的条形码扫描

eBay 在原生应用中有专门的 C++ 库用于条形码的扫描,在 H5 中利用开源 JavaScriptBarcodeReader 做了一个带条形码扫描功能的Web版本。 问题是它只有在 20% 的时间表现良好。 剩余的 80% 的时间运行非常缓慢,准确率也不高。

最终的解决方案是通过 wasm ,将原有的 c++ 库引入,以及业界十分有名的、基于 C 语言编写的开源条形码扫描库 ZBar 引入,再加上原本的 js 库,三者协助,最终识别率达到了 100%

产品上线后的最终效果如下图所示。

产品在上线使用了一段时间后,eBay 技术团队对应用的条形码扫描情况进行了统计,结果发现有 53% 的成功扫描来自于 ZBar34% 来自于自研的 C++ 库。剩下的 13% 则来自于第三方的 JavaScript 库实现。可见,其中通过 Wasm 实现得到的扫描结果占据了总成功次数的 87%

更详细的过程可以参考 WebAssembly在eBay的实践:速度提升50倍。

  1. AutoCAD

AutoCAD 是一款由将近 40 年历史的知名桌面端设计软件,被广泛地用于土木建筑、装饰装潢、工业制图等多个领域中。

最初基于C++ 编译为 Java 代码供 Android 设备使用,最后,在 Google Web Toolkit(一个 Google 开发的可以使用 Java 语言开发 Web 应用的工具集)的帮助下,又将这些 Java 代码转译为了 Web 平台可用的 JavaScript 代码。但最后生成的 Web 应用代码库十分庞大,且在浏览器中的运行性能并不可观。这个「粗糙版」的 Web 应用发布于 2014 年。

2015 年通过 Asm.js 将原有的 C++ 代码中的主要功能直接进行编译移植到到 Web 平台,性能有了很大的提告。20183 月,基于 Wasm 构建的 AutoCAD Web 也成功诞生,https://web.autocad.com/login。

  1. 谷歌地球

Google 地球最初使用 C++ 语言在 Windows 平台上开发。后来移植到了 AndroidiOS 平台中。2017418 日,经过全新设计的 Google 地球 9.0 发布。由于采用了 Native Client 技术,刚发布时仅能在Chrome 中运行。2020227 日,Google 使用 C++ 语言通过 WebAssembly 上重写了 Google 地球,从此 Google 地球可以在 FirefoxEdge 上运行。

  1. bilibili 上传视频的封面

在 知乎 看到的一个回答。

投稿视频的时候,当你的视频还在上传中,已经可以自由选择AI推荐的封面。这里采用了webassembly+AI的前端整合。
webassembly 负责读取本地视频,生成图片;
tensorflow.js 负责加载AI训练过的 model,读取图片并打分。
从完全的 服务端架构 => 前端架构 && 服务端兜底。
webassembly支持解析99%以上的视频编码格式,速度提升体验惠及约50%的web投稿用户。
作者:Stois Fu 链接: 有哪些效果拔群的 WebAssembly 应用? - 知乎 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

由于当前 Wasm 标准下,Wasm 模块不能直接操纵 dom 元素,所以 WebAssembly 主要应用在了一些计算密集型的场景下,视频的解码编码、图像处理、涉及到复杂计算的算法、加密算法等等。

不止于Web

Wasm 除了应用在浏览器中,也可以应用到 out-of-web 环境中。通过 WASIWebAssembly System InterfaceWasm 操作系统接口)标准,Wasm 可以直接与操作系统打交道。通过已经在各种环境实现了 WASI 标准的虚拟机,我们就可以将 wasm 用在嵌入式、IOT 物联网以及甚至云,AI 和区块链等特殊的领域和场景中。

有了 WASI 标准,文章最开始介绍的当前应用的架构在未来可能会发生质的改变。

上边架构的最大问题就是各个操作系统不能兼容,同一个app 需要采用不同的语言在不同平台下各实现一次。

比如一款 A 应用,如果想实现跨平台的话,我们需要用 java 完成在安卓上的开发,用 Objective-C 实现 iOS 上的开发,用 C# 实现 PC 端的开发... ...也就是下边的样子。

但如果有了 wasm ,我们只需要选择任意一门语言,然后编译成 wasm,就可以分发到各个平台上了。

这也是 Wasm 官方宣传的 Ending 定律,Any application that can be compiled to WebAssembly, will be compiled to WebAssembly eventually.

总结

此时回顾一下,WebAssebmly 的定义,应该会清晰很多了。

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

它不是一种语言,而是规定了一种虚拟指令集,可以作为各个语言的编译目标,然后通过 wasm 的虚拟机运行到浏览器还有其他各个平台中。

对于前端领域,当前 Webassembly 在某些场景下可以有效提高前端项目的性能,并且可以将 C/C++ 领域的一些优秀的库通过编译直接运行到浏览器中。如果前端遇到了性能的问题,不妨可以考虑下 WebAssmbly 的方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/185423.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

缺陷分析测试人员绕不开的知识点

缺陷分析也是测试工程师需要掌握的一个能力&#xff0c;但是很多时候大家只记得要提交缺陷、统计缺陷情况&#xff0c;而忽视了缺陷分析。 其实每个项目的缺陷记录都是有很大价值的。在测试阶段分析当前缺陷情况&#xff0c;及时发现存在的问题并调整测试策略&#xff0c;才能…

区间一维dp史上最细总结(绝对干货,还不会的一定要进来)

那年初夏&#xff08;三&#xff09; 注&#xff1a;此部分仅为娱乐和引入用&#xff0c;与本文没有太大关联&#xff0c;可以跳过&#xff0c;阅读下面的正文部分。 上篇出现于&#xff1a;DFS&#xff08;深度优先搜索&#xff09;详解&#xff08;概念讲解&#xff0c;图片…

HashSet源码分析

一、HashSet继承关系 1、继承 public boolean equals(Object o) {if (o this)return true;// o没有实现Set接口&#xff0c;返回falseif (!(o instanceof Set))return false;// 向下转换Collection<?> c (Collection<?>) o;// 元素个数不相等&#xff0c;返回f…

4.组件通讯

默认情况下组件只能使用自己的状态&#xff0c;但当组件拆分的比较小的时候&#xff0c;就不可避免的使用到其他组件的状态&#xff0c;比如之前做的例子&#xff0c;当我们的发表评论区域与显示评论区域拆分为两个组件时&#xff0c;这两个组件之间一定要进行通讯以达成某些功…

π122M30代替Si8621AB-B-IS 低功耗,高能效、抗干扰能力好的 双通道数字隔离器解决方案

π122M30代替Si8621AB-B-IS 低功耗&#xff0c;高能效、抗干扰能力好的解决方案电路简单、稳定性更高 &#xff0c;具有出色的性能特征和可靠性&#xff0c;整体性能优于光耦和基于其他原理的数字隔离器产品。 产品传输通道间彼此独立&#xff0c;可实现多种传输方向的配置&…

Day11 C++STL入门基础知识八——stack、queue容器 基本概念-常用接口 【全面深度剖析+例题代码展示】

&#x1f483;&#x1f3fc; 本人简介&#xff1a;男 &#x1f476;&#x1f3fc; 年龄&#xff1a;18 &#x1f6a9; 今日留言&#xff1a;亮亮被迫去练科目二啦&#xff0c;定时发布的文章&#xff0c;回来统一给大家三连回复嗷~&#x1f609; 文章目录1. stack容器——栈1.…

CSDN常见问题汇总

1.怎么申请退款&#xff1f; 通过CSDN平台购买的“VIP会员、余额”&#xff0c;在刚购买后未使用的情况可支持退款&#xff1b; “付费资源、付费专栏、盲盒、魔盒、课程、C认证”等虚拟商品一经购买后&#xff0c;除了特殊原因外&#xff0c;概不支持退款&#xff1b; 特殊原…

美团8年测试经验,一文手把手教你抒写接口测试框架集成测试报告

在接口自动化测试完成后&#xff0c;通常我们都需要一个测试报告来进行结果展示&#xff0c;而测试报告的美观程度直接决定了你在同事和领导眼中的技术形象&#xff0c;本文将介绍rest-assured接口测试框架集成ExtentReports测试报告&#xff0c;让你的框架更加完美。 ExtentR…

【大唐杯备考】——5G网元功能与接口(学习笔记)

&#x1f4d6; 前言&#xff1a;本期介绍5G网元功能与接口。 目录&#x1f552; 1. 5G移动通信系统整体网络架构&#x1f558; 1.1 5G核心网架构&#x1f558; 1.2 5G接入网架构&#x1f552; 2. 5G主要网元功能&#x1f558; 2.1 UPF&#xff08;用户面功能&#xff09;&#…

使用code-server为Docker容器搭建在线开发环境

Code-server是一个基于服务端的开源VSCode。只要服务器端配置好code-server&#xff0c;就可以在任何浏览器上使用VScode访问服务器的代码进行编程。&#xff08;GitHub地址&#xff1a;https://github.com/cdr/code-server&#xff09; Docker是一个开源的Linux容器引擎。我们…

DeepLabV3+:搭建Mobilenetv2网络

目录 Mobilenetv2的介绍 Mobilenetv2的结构 Inverted Residual Block倒残差结构 Pytorch实现Inverted Residual Block 搭建Mobilenetv2 Pytorch实现Mobilenetv2主干网络 相关参考资料 Mobilenetv2的介绍 Mobilenetv2网络设计基于Mobilenetv1&#xff0c;它保持了其简单…

【进击的算法】动态规划——01背包

&#x1f37f;本文主题&#xff1a;动态规划 01背包 背包问题 C/C 算法 &#x1f388;更多算法&#xff1a;基础回溯算法 基础动态规划 &#x1f495;我的主页&#xff1a;蓝色学者的主页 文章目录一、前言二、概念✔️动态规划概念✔️01背包的概念三、问题描述与讲解&#x1…

spring 中 mybaits 的一级缓存失效

mybatis 的一级缓存 简单回顾下mybatis的一级缓存 本质上是一个基于map实现的内存级别的缓存&#xff0c;默认开启&#xff0c;生命周期是 sqlsession 级别的 为什么会失效 其实这个问题反向分析一下就会有思路了&#xff0c;一级缓存默认是sqlsession级别的&#xff0c;这个规…

2022年rust杂记

以下记录的是&#xff0c;我在学习中的一些学习笔记&#xff0c;这篇笔记是自己学习的学习大杂烩&#xff0c;主要用于记录&#xff0c;方便查找1、相关学习链接https://www.rust-lang.org/zh-CN/governance/ RUST 官网博客https://kaisery.github.io/trpl-zh-cn/&#xff08;最…

应用性能监控对DMS系统综合分析案例

背景 DMS系统是某汽车集团的经销商在线系统&#xff0c;是汽车集团的重要业务系统。本次分析重点针对DMS系统性能进行分析&#xff0c;以供安全取证、性能分析、网络质量监测以及深层网络分析。 该汽车总部已部署NetInside流量分析系统&#xff0c;使用流量分析系统提供实时和…

好好的系统,为什么要分库分表?

不急于上手实战 ShardingSphere 框架&#xff0c;先来复习下分库分表的基础概念&#xff0c;技术名词大多晦涩难懂&#xff0c;不要死记硬背理解最重要&#xff0c;当你捅破那层窗户纸&#xff0c;发现其实它也就那么回事。 什么是分库分表 分库分表是在海量数据下&#xff0…

51单片机学习笔记-14 ADDA

14 ADDA [toc] 注&#xff1a;笔记主要参考B站江科大自化协教学视频“51单片机入门教程-2020版 程序全程纯手打 从零开始入门”。 注&#xff1a;工程及代码文件放在了本人的Github仓库。 14.1 AD/DA简介 14.1.1 AD/DA基本介绍 AD&#xff08;Analog to Digital&#xff09;…

FreeRTOS任务管理

RTOS 的核心是如果高效管理各个任务及任务之间通信&#xff0c;本章将向大家介绍 FreeRTOS 的任务管理&#xff0c;通过本章的学习&#xff0c;让大家对 RTOS 任务的理解更加深入&#xff0c; 为后面的学习做好铺垫。本章分为如下几部分内容&#xff1a; 1 任务管理介绍 2 常用…

ue4c++日记7(动画蓝图)

FVector Speed Pawn->GetVelocity();//获取方向向量FVector xyspeed FVector(Speed.X, Speed.Y,0);//不要z方向MovementSpeed xyspeed.Size();//xy取长//角色是否处于下落状态IsJumping Pawn->GetMovementComponent()->IsFalling();//#include "GameFramewor…

FreeRTOS中的信号量实验

信号量是操作系统中重要的一部分&#xff0c;信号量一般用来进行资源管理和任务同 步&#xff0c;FreeRTOS 中信号量又分为二值信号量、计数型信号量、互斥信号量和递归 互斥信号量。不同的信号量其应用场景不同&#xff0c;但有些应用场景是可以互换着使用。 本章要实现的功能…