当前位置: 首页 > news >正文

浏览器指纹

引言

先看下 官网 给的定义。

WebAssembly (abbreviatedWasm) 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是由Web和Assembly两个词构成,其中Web表明它一定和前端有关。Assembly的意思是汇编,汇编对应机器码,而机器码和CPU的指令集有关,接下来补一下相关的知识。

相关概念

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

复杂指令集是x86、x64(也叫 x86-64, amd64)两种架构,专利在Intel和AMD两家公司手里, 该架构CPU主要是Intel和AMD两家公司,这种CPU常用在PC机上,包括Windows,macOS和Linux。

简单指令集是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   %ebxmov    %eax, [%esp+8] mov    %ebx, [%esp+12]add    %eax, %ebx pop    %ebx ret  _main:push   3push   2call   _add_a_and_b add    %esp, 8ret

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

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

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

历史

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

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

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

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

NaCl/PNaCl

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

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

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

ASM.js

ASM.js是Mozilla在2013年推出的,是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;
}

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

这看起来和TypeScript很像,但其实不是一种东西。TypeScript是js的一个超集,浏览器并不能直接执行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 众多技术标准中的一员。

WebAssembly于2019年12月5日成为万维网联盟(W3C)的推荐标准,与HTML,CSS和JavaScript一起成为Web的第四种语言。

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

初体验

内部结构

目前已经有了将C/C++、Rust、ts、C#、Go、Kotlin、Swift等语言转换为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的模块。然后会分很多Section,Function Section,Code 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 1local.set 1local.get 0i32.const 2i32.ge_sif (result i32)  ;; label = @1i32.const 0local.set 1loop  ;; label = @2local.get 0i32.const -1i32.addcall 1local.get 1i32.addlocal.set 1local.get 0i32.const 3i32.gt_slocal.set 2local.get 0i32.const -2i32.addlocal.set 0local.get 2br_if 0 (;@2;)endlocal.get 1i32.const 1i32.addelsei32.const 1end)(func (;2;) (type 0) (result i32)global.get 0)(func (;3;) (type 3) (param i32)local.get 0global.set 0)(func (;4;) (type 1) (param i32) (result i32)global.get 0local.get 0i32.subi32.const -16i32.andlocal.tee 0global.set 0local.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提供的api,Web提供的api支持流式编译实例化。

js的方法,

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

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

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

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

web的方法,

WebAssembly.instantiateStreaming(source, importObject)

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

其他参数和返回值和js的api均一致。

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.exportsconsole.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中利用开源JavaScript库BarcodeReader做了一个带条形码扫描功能的Web版本。问题是它只有在20%的时间表现良好。剩余的80%的时间运行非常缓慢,准确率也不高。最终的解决方案是通过wasm,将原有的c++库引入,以及业界十分有名的、基于C语言编写的开源条形码扫描库ZBar引入,再加上原本的js库,三者协助,最终识别率达到了100%。产品上线后的最终效果如下图所示。

产品在上线使用了一段时间后,eBay技术团队对应用的条形码扫描情况进行了统计,结果发现有53%的成功扫描来自于ZBar;34%来自于自研的C++库。剩下的13%则来自于第三方的JavaScript库实现。可见,其中通过Wasm实现得到的扫描结果占据了总成功次数的87%。更详细的过程可以参考 WebAssembly在eBay的实践:速度提升50倍。

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

最初基于C++ 编译为Java代码供Android设备使用,最后,在Google Web Toolkit(一个Google开发的可以使用Java语言开发Web应用的工具集)的帮助下,又将这些Java代码转译为了Web平台可用的JavaScript代码。但最后生成的Web应用代码库十分庞大,且在浏览器中的运行性能并不可观。这个「粗糙版」的Web应用发布于2014年。2015年通过Asm.js将原有的C++代码中的主要功能直接进行编译移植到到Web平台,性能有了很大的提告。2018年3月,基于Wasm构建的AutoCAD Web也成功诞生,https://web.autocad.com/login。

  1. 谷歌地球Google地球最初使用C++语言在Windows平台上开发。后来移植到了Android和iOS平台中。2017年4月18日,经过全新设计的Google地球9.0发布。由于采用了 Native Client 技术,刚发布时仅能在Chrome中运行。2020年2月27日,Google使用C++语言通过WebAssembly上重写了Google地球,从此Google地球可以在Firefox和Edge上运行。
  2. bilibili 上传视频的封面在 知乎 看到的一个回答。

投稿视频的时候,当视频还在上传中,已经可以自由选择AI推荐的封面。这里采用了webassembly+AI的前端整合。

webassembly 负责读取本地视频,生成图片;

tensorflow.js 负责加载AI训练过的 model,读取图片并打分。

从完全的服务端架构 => 前端架构 && 服务端兜底。

webassembly支持解析99%以上的视频编码格式,速度提升体验惠及约50%的web投稿用户。

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

不止于Web

Wasm除了应用在浏览器中,也可以应用到out-of-web环境中。通过WASI(WebAssembly System Interface,Wasm操作系统接口)标准,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 (abbreviatedWasm) 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的方案。

相关文章:

浏览器指纹

引言 先看下 官网 给的定义。 WebAssembly (abbreviatedWasm) 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 …...

W外链平台有什么优势?

W外链作为一种短网址服务&#xff0c;具备多项功能和技术优势&#xff0c;适用于多种场景&#xff0c;以下是其主要特点和优势&#xff1a; 短域名与高级设置&#xff1a;W外链提供了非常短的域名&#xff0c;这有助于提高用户体验&#xff0c;使其在社交媒体分享时更加便捷。…...

深入理解Spring Cache:加速应用性能的秘钥

一、什么是Spring Cache&#xff1f; Spring Cache是Spring框架中的一部分&#xff0c;它为应用提供了一种统一的缓存抽象&#xff0c;可以轻松集成各种缓存提供者&#xff08;如Ehcache、Redis、Caffeine等&#xff09;。通过使用Spring Cache&#xff0c;开发者可以在方法上…...

C语言入门基础题(力扣):完成旅途的最少时间(C语言版)

1.题目&#xff1a; 给你一个数组 time &#xff0c;其中 time[i] 表示第 i 辆公交车完成 一趟旅途 所需要花费的时间。 每辆公交车可以 连续 完成多趟旅途&#xff0c;也就是说&#xff0c;一辆公交车当前旅途完成后&#xff0c;可以 立马开始 下一趟旅途。每辆公交车 独立 …...

基于LORA的一主多从监测系统_0.96OLED

关联&#xff1a;0.96OLED hal硬件I2C LORA 在本项目中每个节点都使用oled来显示采集到的数据以及节点状态&#xff0c;OLED使用I2C接口与STM32连接&#xff0c;这个屏幕内部驱动IC为SSD1306&#xff0c;SSD1306作为从机地址为0x78 发送数据&#xff1a;起始…...

C#系统学习路线

分享一个C#程序员的成长学习路线规划&#xff0c;希望能够帮助到想从事C#开发的你。 我一直在想&#xff0c;初学者刚开始学习编程时应该学些什么&#xff1f;学习到什么程度才能找到工作&#xff1f;才能在项目中发现和解决Bug&#xff1f; 我不知道每位初学者在学习编程时是…...

UI开发:从实践到探索

UI开发&#xff1a;从实践到探索 参考博客文章&#xff1a;https://blog.jim-nielsen.com/2024/sanding-ui/ 在现代web开发中&#xff0c;用户界面&#xff08;UI&#xff09;的重要性不言而喻。一个优秀的UI不仅能提升用户体验&#xff0c;还能直接影响产品的成功。 UI开发…...

操作系统 | 学习笔记 | 王道 | 3.1 内存管理概念

3 内存管理 3.1 内存管理概念 3.1.1 内存管理的基本原理和要求 内存可以存放数据&#xff0c;程序执行前需要先放到内存中才能被CPU处理—缓和cpu和磁盘之间的速度矛盾 内存管理的概念 虽然计算机技术飞速发展&#xff0c;内存容量也在不断扩大&#xff0c;但仍然不可能将所有…...

Unity射线之拾取物体

实现效果&#xff1a; 可以移动场景内物品放置到某个位置。通过射线检测&#xff0c;点击鼠标左键&#xff0c;移动物体&#xff0c;再点击左键放下物体。 效果&#xff1a; 移动物体 实现思路&#xff1a; 通过射线检测&#xff0c;将检测到的物体吸附到摄像机前的一个空物…...

Python的numpy库矩阵计算(数据分析)

一、创建矩阵 import numpy as np#创建矩阵anp.arange(15).reshape(3,5) bnp.arange(15,30).reshape(3,5) 使用arrange和reshape创建的二维数组就可以看成矩阵。 此时a和b存储的是&#xff1a; [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14]] [[15 16 17 18 19]…...

R语言的基本语句及基本规则

0x01 赋值语句 使用 “<-” 或 “” 进行赋值。例如&#xff1a; x <- 5 # 将数值 5 赋值给变量 x y 10 # 另一种赋值方式0x02 输出语句 使用 print() 函数输出内容。例如&#xff1a; print("Hello, R!") print(x)0x03 注释语句 任何在 #之后的内容在…...

网络受限情况下安装openpyxl模块提示缺少Jdcal,et_xmlfile

1.工作需要处理关于Excel文件内容的东西 2.用公司提供的openpyxl模块总是提示缺少jdcal文件,因为网络管控,又没办法直接使用命令下载&#xff0c;所以网上找了资源&#xff0c;下载好后上传到个人资源里了 资源路径 openpyxl jdcal et_xmlfile 以上模块来源于&#xff1a;Py…...

【算法】- 查找 - 散列表查询(哈希表)

文章目录 前言一、哈希表的思想二、哈希表总结 前言 散列技术&#xff1a;在记录的存储位置和它的关键字之间建立一个确定的对应关系f&#xff0c;使得每个关键字key对应一个存储位置f(key) 哈希表&#xff1a;采用散列技术将记录存储在一块连续的存储空间中&#xff0c;这块连…...

货币政策工具

本文为个人学习笔记&#xff0c;内容源于教材&#xff1b;整理记录的同时也作为一种分享。 1. 简介 货币政策工具作为央行实现货币政策目标的经济手段&#xff0c;以期达到最终目标&#xff0c;即物价稳定&#xff0c;充分就业&#xff0c;经济增长&#xff0c;国际收支平衡。…...

std::async概念和使用方法

std::async是 C 标准库中的一个函数模板&#xff0c;用于启动一个异步任务&#xff0c;并返回一个std::future对象&#xff0c;该对象可用于获取异步任务的结果。 1、概念 std::async允许你以异步的方式执行一个函数或者可调用对象&#xff0c;它会在后台启动一个新的线程或者…...

Chatgpt 原理解构

一、背景知识 1. 自然语言处理的发展历程 自然语言处理在不同时期呈现出不同的特点和发展态势。萌芽期&#xff0c;艾伦・图灵在 1936 年提出 “图灵机” 概念&#xff0c;为计算机诞生奠定基础&#xff0c;1950 年他提出著名的 “图灵测试”&#xff0c;预见了计算机处理自然…...

【每日刷题】Day135

【每日刷题】Day135 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. LCR 011. 连续数组 - 力扣&#xff08;LeetCode&#xff09; 2. 【模板】二维前缀和_牛客题霸_牛客…...

Linux运维01:VMware创建虚拟机

视频链接&#xff1a;05.新建VM虚拟机_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1nW411L7xm/?p14&spm_id_from333.880.my_history.page.click&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.点击“创建虚拟机” 2.选择“自定义&#xff08;高级&#xff0…...

服务器平均响应时间和数据包大小关系大吗?

服务器的平均响应时间与数据包大小有一定的关系&#xff0c;但这只是影响响应时间的众多因素之一。具体来说&#xff0c;数据包大小对服务器响应时间的影响可以从以下几个方面来理解&#xff1a; 1. 数据传输时间 影响: 较大的数据包需要更多的时间在网络上传输&#xff0c;因此…...

Vue入门-指令学习-v-show和v-if

v-show&#xff1a; 作用&#xff1a;控制元素的显示隐藏 语法&#xff1a;v-show"表达式" 表达式值true显示&#xff0c;false隐藏 v-if 作用&#xff1a;控制元素的显示隐藏&#xff08;条件渲染&#xff09; 语法&#xff1a; vif"表达式" 表达式tr…...

nacos多数据源插件介绍以及使用

概述 在微服务架构中&#xff0c;服务配置的集中管理和动态调整是至关重要的。Nacos 提供了配置管理和服务发现的功能&#xff0c;其中配置管理支持动态数据源的切换&#xff0c;增强了其在复杂环境中的适用性。默认情况下&#xff0c;Nacos 支持 MySQL 和Derby&#xff0c;但…...

国庆档不太热,影视股“凉”了?

今年国庆档票房止步21亿元&#xff0c;属实有点差强人意。 根据国家电影局统计&#xff0c;2024年国庆档&#xff08;2024年10月1日至7日&#xff09;全国电影票房为21.04亿元&#xff0c;观影人次为5209万&#xff0c;总票房成绩、观影总人次同比均有所下滑。 作为传统观影高…...

QtDesign预览的效果与程序运行的结果不一致的解决方法

存在的问题 使用Qt designer软件设计出来的界面&#xff0c;与转换成python程序运行出来的结果不一致&#xff0c;具体看下图 Qt designer预览结果 程序运行出来的结果 原因分析 我自己的电脑是2560*1600分辨率的屏幕&#xff0c;采用的是200%的缩放比例&#xff0c;出现这种…...

模运算和快速幂

文章目录 模运算快速幂 模运算 模运算是大数运算中的常用操作。如果一个数太大&#xff0c;无法直接输出&#xff0c;或者不需要直接输出&#xff0c;则可以对它取模&#xff0c;缩小数值再输出。取模可以防止溢出&#xff0c;这是常见的操作。 取模运算一般要求a和m的符号一…...

【机器学习】——神经网络与深度学习:从基础到应用

文章目录 神经网络基础什么是神经网络&#xff1f;神经网络的基本结构激活函数 深度学习概述什么是深度学习&#xff1f;常见的深度学习算法 深度学习的工作流程深度学习的实际应用结论 引言 近年来&#xff0c;神经网络和深度学习逐渐成为人工智能的核心驱动力。这类模型模仿人…...

Unity各个操作功能+基本游戏物体创建与编辑+Unity场景概念及文件导入导出

各个操作功能 部分功能 几种操作游戏物体的方式&#xff1a; Center:有游戏物体父子关系的时候&#xff0c;中心点位置 Global/Local:世界坐标系方向/自身坐标系方向 &#xff1a;调试/暂停/下一帧 快捷键 1.Alt鼠标左键&#xff1a;可以实现巡游角度查看场景 2.鼠标滚轮…...

QT入门教程攻略 QT入门游戏设计:贪吃蛇实现 QT全攻略心得总结

Qt游戏设计&#xff1a;贪吃蛇 游戏简介 贪吃蛇是一款经典的休闲益智类游戏&#xff0c;玩家通过控制蛇的移动来吃掉地图上的食物&#xff0c;使蛇的身体变长。随着游戏的进行&#xff0c;蛇的移动速度会逐渐加快&#xff0c;难度也随之增加。当蛇撞到墙壁或自己的身体时&…...

Linux No space left on device分析和解决

报错解释&#xff1a; "No space left on device" 错误表示你的Linux设备&#xff08;通常是磁盘分区&#xff09;上没有剩余空间了。这可能是因为磁盘已满&#xff0c;或者inode已满。磁盘空间是指磁盘上的实际空间&#xff0c;而inode是用来存储文件元数据的数据结…...

Qt实现Halcon窗口显示当前图片坐标

一、前言 Halcon加载图片的窗口&#xff0c;不仅能放大和缩小图片&#xff0c;还可以按住Ctrl键显示鼠标下的灰度值&#xff0c;这种方式很方便我们分析缺陷的灰度和对比度。 二、实现方式 ① 创建显示坐标和灰度的widget窗口 下图的是widget部件&#xff0c;使用了4个label控…...

构建宠物咖啡馆:SpringBoot框架的实现策略

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于Spring Boot的宠物咖啡馆平台的设计与…...