WebAssembly(WASM)性能逊于Shell?求对比WAT文件及解惑
WebAssembly性能疑惑解析与CLI可用WAT示例
为什么你的WASM程序更慢?
- 瓶颈不在计算,在IO:你测试的核心操作是循环打印1000万条数据,这个场景下IO开销占了90%以上,WASM的计算优势根本发挥不出来。反而sh脚本依赖系统原生的输出逻辑,和系统IO的交互更直接高效;而emcc编译的程序默认会通过emscripten的封装层转发输出,额外的调用链路带来了不小的性能损耗。
- 编译优化没开启:emcc默认是Debug编译模式,没有做任何性能优化。如果加上
-O2或-O3参数编译,WASM的执行效率会大幅提升——虽然IO瓶颈还是存在,但计算部分的开销会被压到最低。 - WASM的性能优势场景用错了:WASM擅长的是CPU密集型计算(比如复杂算法、大数据处理),不是频繁读写终端/文件的IO密集型任务。只有当程序核心是纯计算时,WASM的性能才会接近甚至超过原生程序。
Linux CLI可用的WAT性能对比示例
下面是一个计算1到1亿整数和的WAT程序(保存为sum.wat),可以用wasmtime或wasmer直接运行,这个场景能体现WASM的计算性能优势:
(module (func $main (export "_start") (local $i i64) (local $sum i64) (local.set $i (i64.const 1)) (local.set $sum (i64.const 0)) (loop $loop (local.set $sum (i64.add (local.get $sum) (local.get $i))) (local.set $i (i64.add (local.get $i) (i64.const 1))) (br_if $loop (i64.lt_s (local.get $i) (i64.const 100000001))) ) (call $print_i64 (local.get $sum)) ) (func $print_i64 (param $n i64) (local $buf i32) (local $len i32) (local $ptr i32) (local.set $buf (call $malloc (i32.const 20))) (local.set $ptr (local.get $buf)) (if (i64.eq (local.get $n) (i64.const 0)) (then (i32.store8 (local.get $ptr) (i32.const 48)) (local.set $len (i32.const 1)) ) (else (local $temp i64) (local.set $temp (local.get $n)) (local.set $len (i32.const 0)) (loop $reverse (i32.store8 (local.get $ptr) (i32.add (i32.const 48) (i32.wrap_i64 (i64.rem_u (local.get $temp) (i64.const 10))))) (local.set $ptr (i32.add (local.get $ptr) (i32.const 1))) (local.set $len (i32.add (local.get $len) (i32.const 1))) (local.set $temp (i64.div_u (local.get $temp) (i64.const 10))) (br_if $reverse (i64.ne (local.get $temp) (i64.const 0))) ) (local $start i32) (local $end i32) (local.set $start (local.get $buf)) (local.set $end (i32.sub (i32.add (local.get $buf) (local.get $len)) (i32.const 1))) (loop $swap (if (i32.lt (local.get $start) (local.get $end)) (then (local $temp_byte i32) (local.set $temp_byte (i32.load8_u (local.get $start))) (i32.store8 (local.get $start) (i32.load8_u (local.get $end))) (i32.store8 (local.get $end) (local.get $temp_byte)) (local.set $start (i32.add (local.get $start) (i32.const 1))) (local.set $end (i32.sub (local.get $end) (i32.const 1))) (br $swap) ) ) ) ) ) (call $fd_write (i32.const 1) (local.get $buf) (i32.const 1) (i32.const 0) ) (call $free (local.get $buf)) (local.set $buf (call $malloc (i32.const 1))) (i32.store8 (local.get $buf) (i32.const 10)) (call $fd_write (i32.const 1) (local.get $buf) (i32.const 1) (i32.const 0) ) (call $free (local.get $buf)) ) (import "wasi_snapshot_preview1" "malloc" (func $malloc (param i32) (result i32))) (import "wasi_snapshot_preview1" "free" (func $free (param i32))) (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) (memory 1) )
运行步骤:
- 安装
wasmtime:Linux下用包管理器,比如apt install wasmtime或dnf install wasmtime - 将代码保存为
sum.wat - 编译为WASM:
wasmtime compile sum.wat -o sum.wasm - 运行并计时:
time wasmtime sum.wasm
你可以对比用C写的同功能程序(编译时加-O3)和sh脚本(用循环计算和会慢很多),就能直观看到WASM在计算密集场景下的性能优势。
内容的提问来源于stack exchange,提问作者aze2201
相关产品推荐
相关产品推荐

