前几天,尝试了在Deno环境下JavaScript与集成WebAssembly的集成,我也立马将整个过程记录到博客里。
没多久,有朋友私信我说:“你只是验证方案的可行性,并没有证明这么做的必要性”。的确如此,其实,我故意卖关子,今天再写一篇把使用WebAssembly的“必要性”给点出来。(因为我需要花点时间学习Rust)
所谓的“必要性”,说白了就是性能的对比。看看使用了WebAssembly这样复杂性的方案,能不能获得应用程序性能的等比例提升,性价比几何。
啃了两天Rust之后,我已经准备好了!坐稳,开车~~
用例说明
我的测试用例是这样的:
- 用字母和数字组成的长度为64的随机字符串,生成图片二维码
- 将图片二维码用base64的编码输出
- 以上过程循环1000次,记录试程序执行时长
测试代码
下面有请三组选手上场:
1、Deno(TypeScript)
import { qrcode } from "https://deno.land/x/qrcode/mod.ts";
// 生成一个随机的64位字符串
const generateRandomtring = (len:number=64): string =>{
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var randomString = "";
for (let i = 0; i < len; i++) {
const n = Math.floor(Math.random() * charset.length);
randomString += charset[n];
}
return randomString;
}
const generateQRCode = async () => {
for (let i = 0; i < 1000; i++) {
const input = generateRandomtring();
const base64Image = await qrcode(input,{size:160});
}
}
const startTime = Date.now();
generateQRCode();
const endTime = Date.now();
console.log(`Deno Time taken: ${endTime - startTime} ms`);
2、Node.js(JavaScript)
Node.js作为Deno的姐妹,也前来助阵。生成二维码也是用的第三方的库:
{
"dependencies": {
"qrcode": "^1.5.4"
}
}
const QRCode = require('qrcode');
const generateRandomtring = (len) =>{
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var randomString = "";
for (let i = 0; i < len; i++) {
const n = Math.floor(Math.random() * charset.length);
randomString += charset[n];
}
return randomString;
}
const generateQRCode = async () => {
for (let i = 0; i < 1000; i++) {
const input = generateRandomtring(64);
var base64Image = await QRCode.toDataURL(input);
}
}
const startTime = Date.now();
generateQRCode();
const endTime = Date.now();
console.log(`Node.js Time taken: ${endTime - startTime} ms`);
3、WebAssembly(Rust)
- Rust引用的第三方库
[dependencies]
wasm-bindgen = "=0.2.92"
qrcode = "0.14.1"
rand = "0.8.4"
base64 = "0.21.0"
getrandom = { version = "0.2", features = ["js"] }
- 生成二维的Rust代码
use wasm_bindgen::prelude::*;
use qrcode::QrCode;
use base64::prelude::*;
use rand::{thread_rng, Rng};
fn generate_random_string(length: usize) -> String {
let charset: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
let mut rng = thread_rng();
let mut result = String::with_capacity(length);
for _ in 0..length {
let random_index: usize = rng.gen_range(0..charset.len());
result.push(charset[random_index] as char);
}
return result
}
#[wasm_bindgen]
pub fn gen_qrcode() -> String{
let random_string = generate_random_string(64);
println!("{}", random_string);
// 生成二维码
let code = QrCode::new(&random_string).unwrap();
let image= code.render::<qrcode::render::unicode::Dense1x2>().build();
let img_bytes = image.as_bytes();
// 将二维码字符串转换为Base64编码
let base64_encoded = BASE64_STANDARD.encode(img_bytes);
return base64_encoded;
}
- Deno调用
gen_qrcode
import { instantiate } from "./lib/lib_qrcode.generated.js";
const { gen_qrcode } = await instantiate();
const startTime = Date.now();
for (let i = 0; i < 1000; i++) {
gen_qr_code()
}
const endTime = Date.now();
console.log(`WASM Time taken: ${endTime - startTime}ms`);
测试结果与分析
以上三组代码各自运行3次,取平均值。结果如下:
Deno(TypeScript) | Node.js(JavaScript) | WebAssembly(Rust) |
---|---|---|
6.1s | 4.3s | 1.4s |
- 不出意外,WebAssembly以好几个身位的绝对优势遥遥领先!证明了它是一套值得我们拥有的高性能的应用开发方案。
- Node.js居然稍稍领先比Deno,这个有点始料不及。个人分析,有可能受第三方qrcode的库的拖累,导致Deno掉了链子。如果抛开这个因素,估计Deno和Node.js应当是在伯仲之间的。
结论
我们用代码实例去对比,证明了Rust+WebAssembly强悍的性能。相信之前有所怀疑的小伙伴们,现在也跟我一样的笃定,正在摩拳擦掌,准备开始学习Rust了吧!