WebAssembly简述

WebAssembly简述


webassembly 性能优化 前端

JavaScript 是解释型语言,也是动态类型语言。如果变量类型是在运行时决定的,那么就是动态类型语言。

相对于动态类型语言,还有静态类型语言,C++, Java 等就是静态类型语言,其变量类型是在定义的时候就决定了的。

int x = 1;

通过一条指令,编译器就能知道变量 x 的类型和内存位置。但是对于 JavaScript 中相同的操作,每次执行程序时,引擎都必须检查它是整数还是浮点数,或者任何其他有效的数据类型。所以 JavaScript 中的每条指令都要经过几次类型检查和转换,这会影响到它的执行速度。

下面是 JavaScript 在运行代码时花费的时间:

JS执行过程

下面是 WebAssembly 花费的时间:

WASM执行过程

什么是 WebAssembly?

浏览器只能运行 JavaScript,但如果我们有一个虚拟微处理器,它可以将任何高级语言转换成可以在所有主流浏览器上运行的机器码,那会怎么样呢?这正是 Web 汇编(web assembly)所做的事情。

WASM 不是一种编程语言。简而言之,它是一种将用一种编程语言编写的代码转换为浏览器可理解的机器代码的技术。

WASM (WebAssembly 的缩写)被设计为其他语言的编译目标,允许服务器端代码(如 C 或 C++代码)被编译成 WASM 并在浏览器中执行。

下面是一个用 C++编写并被转为 WASM 的加法函数的例子。

WASM 不能直接与 DOM 交互,因此我们需要同时使用 JavaScript 和 WASM。

首先用 C++写一个斐波那契数列的方法

int fib(int n)
{
   if (n <= 1)
      return n;
   return fib(n-1) + fib(n-2);
}

然后使用 WebAssembly Explorer 将 C++文件转换为浏览器能够理解的预编译 WASM 模块。 https://mbebenita.github.io/WasmExplorer/ 编译完成后大概是这样:

(module
 (table 0 anyfunc)
 (memory $0 1)
 (export "memory" (memory $0))
 (export "_Z3fibi" (func $_Z3fibi))
 (func $_Z3fibi (; 0 ;) (param $0 i32) (result i32)
  (block $label$0
   (br_if $label$0
    (i32.ge_s
     (get_local $0)
     (i32.const 2)
    )
   )
   (return
    (get_local $0)
   )
  )
  (i32.add
   (call $_Z3fibi
    (i32.add
     (get_local $0)
     (i32.const -1)
    )
   )
   (call $_Z3fibi
    (i32.add
     (get_local $0)
     (i32.const -2)
    )
   )
  )
 )
)

然后下载下来 wasm 文件。

然后写一个 JS 文件,执行方法,对比一下 JS 和 WASM 执行的性能。

function loadWebAssembly(fileName) {
  return fetch(fileName)
    .then((response) => response.arrayBuffer())
    .then((buffer) => WebAssembly.compile(buffer))
    .then((module) => {
      return new WebAssembly.Instance(module);
    });
}

let fibc;

loadWebAssembly("fib.wasm").then((instance) => {
  // ._Z3fibi是wasm中export出来的名字
  // fibc是c++ 斐波那契数列
  fibc = instance.exports._Z3fibi;
  // perfoFnc(fibc, 45, "WASM");
});

// JS方法 斐波那契数列
function fibj(n) {
  if (n <= 1) return n;
  return fibj(n - 1) + fibj(n - 2);
}

function perfoFnc(fnc, n, remark) {
  var startTime = performance.now();
  var res = fnc(n);
  var endTime = performance.now();
  console.log(
    `Functino ${remark} took ${
      endTime - startTime
    } milliseconds,result is ${res}`
  );
}

// perfoFnc(fibj, 45, "JS");

然后写个 html 文件,引入该 JS 文件,本地起一个服务看下效果:

运行对比结果

可以看到 fibj()(用JavaScript编写) 所花费的时间比 fibc()(用WebAssembly编写) 更长

© 2025 Niko Xie