WebAssembly究竟快不快

前言

    如果还不了解WebAssembly是什么的,可以查阅我的另一篇博客:走进WebAssembly。本节内容主要探讨WebAssembly相比于js的执行效率究竟如何,并且定论适不适合应运于项目中,以及适合应用于哪类项目。当然,本节依旧采用rust语言编译WebAssembly。

case1:循环+累加

    现有如下三种方式 js+forjs+whilewasm+for,我们将在循环次数为十、千、十万、千万的场景中分别各自执行五次,对比其结果。
    js+for

const js_for = () => {
    let v = 1;
    for(let i = 0; i < 10; i++){
        v += i;
    };
    return v
}  
console.time('js-for')
js_for();
console.timeEnd('js-for')

    js+while

const js_while = () => {
    let i = 0
    let v = 1;
    while(i < 10){
        v += i;
        i++
    }
    return v
}  
console.time('js-while')
js_while();
console.timeEnd('js-while')

    wasm+for

import init, { wasm_for } from "../layout/pkg/layout.js";

init().then(() => {
    console.time('wasm-for')
    wasm_for();
    console.timeEnd('wasm-for')
});
#[wasm_bindgen]
pub fn wasm_for() {
    let mut v = 1;
    for i in 0..10{
        v += i;
    };
}

    结果对比(以下数据单位均为毫秒ms)

循环十次js+forjs+whilewasm+for
第一次0.023925781250.020019531250.01416015625
第二次0.0070800781250.00292968750.011962890625
第三次0.00292968750.0019531250.01904296875
第四次0.00488281250.0041503906250.02001953125
第五次0.0090332031250.00292968750.011962890625
平均0.00957031250.0063964843750.0154296875
循环千次js+forjs+whilewasm+for
第一次0.028808593750.023925781250.01513671875
第二次0.0080566406250.007324218750.01513671875
第三次0.0168457031250.013183593750.0107421875
第四次0.014160156250.0100097656250.012939453125
第五次0.0080566406250.00683593750.030029296875
平均0.0151855468750.0122558593750.016796875
循环十万次js+forjs+whilewasm+for
第一次2.8942871093752.6911621093750.013916015625
第二次1.189941406251.2351074218750.010986328125
第三次1.0097656251.3679199218750.010986328125
第四次0.9689941406251.160156250.010986328125
第五次1.1191406251.232910156250.011962890625
平均1.436425781251.5374511718750.011767578125
循环千万次js+forjs+whilewasm+for
第一次8.43090820312529.0197753906250.01708984375
第二次8.3381347656258.435058593750.010986328125
第三次8.729980468758.522949218750.01904296875
第四次8.71411132812532.1252441406250.018310546875
第五次8.9768066406258.641113281250.01318359375
平均8.6379882812517.3488281250.01572265625

    将以上数据均值归入折线图对比,可明显发现循环次数越大时,js执行耗时耗时越大,而WebAssembly似乎并受到循环次数增加而增加其运行时间。由此可见当循环次数越大时,WebAssembly非常优于js。
    如果你仔细看了上述数据,可以发现在循环千万次的js+while中,运行时长波动极大,故而可以给自己留一个心眼。

请添加图片描述
    既然WebAssembly这么快,那为什么还没有普及?不急,继续往下看。

case2:传参循环

    在case1中我们有看到WebAssembly的强大,但在实际开发过程中我们是无法离开js的,要想使用WebAssembly,需要我们将js中的数据传入到WebAssembly的方法中处理,并返回。
    ok,现有如下四种方式 js+forjs+whilejs+forEachwasm+for,我们这次直接查看十万次数据对比。
    函数入参生成

const rand = (n: number, m: number) => {
    var c = m - n + 1
    return Math.floor(Math.random() * c + n)
}
let arr: Array<{x: number, y: number}> = []
for(let i = 0; i < 10000000; i++){
    arr.push({
        x: rand(0, 10),
        y: rand(0, 10)
    })
};

    js+for

const js_for = (arr: Array<{x: number, y: number}>) => {
    let x = 0;
    let y = 0;
    let length = arr.length
    for(let i = 0; i < length; i++){
        let arrI = arr[i]
        if(arrI){
            x += arrI.x
            y += arrI.y
        }
    };
    console.log('js_for', x, y)
}  
console.time('js_for')
js_for(arr);
console.timeEnd('js_for')

    js+while

const js_while = (arr: Array<{x: number, y: number}>) => {
    let x = 0;
    let y = 0;
    let length = arr.length
    let i = 0
    while(i < length){
        let arrI = arr[i]
        if(arrI){
            x += arrI.x
            y += arrI.y
        }
        i++
    }
    console.log('js_while', x, y)
}  
console.time('js_while')
js_while(arr);
console.timeEnd('js_while')

    js+forEach

const js_forEach = (arr: Array<{x: number, y: number}>) => {
    let x = 0;
    let y = 0;
    arr.forEach(item => {
        if(item){
            x += item.x
            y += item.y
        }
    })
    console.log('js_forEach', x, y)
}  
console.time('js_forEach')
js_forEach(arr);
console.timeEnd('js_forEach')

    wasm+for

import init, { wasm_for } from "../layout/pkg/layout.js";

init().then(() => {
    console.time('wasm_for')
    wasm_for();
    console.timeEnd('wasm_for')
});
#[derive(Serialize, Deserialize)]
struct Point {
    x: i32,
    y: i32,
}
#[wasm_bindgen]
pub fn wasm_for(arr: JsValue) -> Result<JsValue, JsValue>{
    let arr: Vec<Point> = from_value(arr).unwrap();
    let mut x = 0;
    let mut y = 0;
    for i in 0..arr.len() {
        if let Some(arr_i) = arr.get(i) {
            x += arr_i.x;
            y += arr_i.y;
        }
    }
    let result = Point { x: x, y: y };
    Ok(to_value(&result).unwrap())
}
循环千万次js+forjs+whilejs+forEachwasm+for
第一次53.79907226562549.403076171875183.839843758930.927978515625
第二次44.26196289062545.554931640625149.686035156258388.2861328125
第三次43.51879882812545.261962890625132.5178222656258432.029052734375

    看到以上数据时,不禁大为吃惊,WebAssembly为什么执行时间会这么大,甚至已经没有了让我求平均值得欲望,我尝试将循环次数降为十次后的数据如下

循环十次js+forjs+whilejs+forEachwasm+for
第一次0.178222656250.1110839843750.1257324218750.5908203125
第二次0.16699218750.1308593750.09472656250.93994140625
第三次0.082031250.0410156250.1191406250.60498046875

    没有任何意外,WebAssembly的执行时间仍然是js执行时间的数倍,那与case1相比区别在何处?没错,就是“let arr: Vec = from_value(arr).unwrap();”这句话。在rust中是无法直接处理js数组对象类数据的,这句话的目的就是将js数据转换为rust可以识别的数据,而性能也恰恰最大的消耗在“无用”的数据转换上,WebAssembly与js的通信桥梁直接崩裂。我只能说如果这个问题无法解决,那将很难走进web。
    这还仅仅是js数据转换rust数据,如果rust数据再转换js数据,那执行时间是不是要翻倍呢?

结论

    WebAssembly快吗?快。WebAssembly能用吗?不能用。
    以上观点仅限本人,如有大哥有提议,可以评论区留言探讨一下啦。
    在web上WebAssembly还有适用的场景吗?我只能想到在有大量数据处理+不频繁数据转换的项目才可能使用,但是有这样的项目吗?

相关知识

在case2的中rust代码中需要引入以下代码

use serde::{Deserialize, Serialize};
use serde_wasm_bindgen::{from_value, to_value};

并且修改Cargo.toml配置

[dependencies]
wasm-bindgen = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde-wasm-bindgen = "0.3"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风舞红枫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值