我编译成 wasm 的 rust 代码比 js 慢,我做错了什么?
My rust code compiled to wasm is slower than js, what did i do wrong?
这是我想用 rust 翻译成 wasm 编译它的原始函数,因为它会使它更快(因为它是我服务器中的热门函数)
export const generateRandomGuid = function (): string {
let guid: string = "0x";
let guidString: string = uuidv4();
const bytes = uuidParse(guidString);
const arrayBytes = new Uint8Array(bytes);
for (let index = 0; index < arrayBytes.length; index++) {
if (guid.length === 18) break;
const byte = arrayBytes[index].toString(16);
if (arrayBytes[index].toString(16).length === 1) {
guid += "0" + byte;
} else {
guid += byte;
}
}
return guid;
};
我在 rust 中是这样翻译的:
use uuid::Uuid;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn generate_random_guid() -> String {
let my_uuid: Uuid = Uuid::new_v4();
let array_bytes = my_uuid.as_bytes();
let mut rand_id: String = String::new();
rand_id.push_str("0x");
for byte in array_bytes {
let formatted_byte: String = format!("{:X}", byte);
if formatted_byte.len() == 1 {
let mut formatted_byte_with_additionnal_zero: String = "0".to_string();
formatted_byte_with_additionnal_zero.push_str(&formatted_byte);
rand_id.push_str(&formatted_byte_with_additionnal_zero);
} else {
rand_id.push_str(&formatted_byte);
}
if rand_id.len() == 18 {
break;
}
}
return rand_id;
}
使用 wasm-pack 和此配置在 wasm 中编译:
[package]
name = "h1emu-core"
version = "0.1.4"
edition = "2018"
[dependencies]
wasm-bindgen = "0.2.45"
uuid = {version = "0.8.2", features = ["v4","wasm-bindgen"], default-features = false }
getrandom = { version = "0.2.3", features = ["js"] }
[lib]
crate-type = ["cdylib"]
[profile.release]
lto = true
opt-level = 3
结果不是想要的,看来js版本比wasm版本快一倍。
所以我问自己是不是我的 rust 代码不好,还是我的配置不好,或者只是我的 wasm 不合适。
代码中的分配数量可以从最坏情况下的 n*2 大大减少到常数 1(忽略 Uuid::new_v4
,我将其视为手头算法外部的常数因子)通过使用 write!
宏和格式说明符:
pub fn generate_random_guid() -> String {
use std::fmt::Write;
let my_uuid: Uuid = Uuid::new_v4();
let array_bytes = my_uuid.as_bytes();
let mut rand_id = String::with_capacity(18);
rand_id.push_str("0x");
for byte in &array_bytes[..8] {
write!(&mut rand_id, "{:02X}", byte).unwrap();
}
rand_id
}
虽然分配可能占运行时间的大部分,但所有格式化机制肯定也无济于事。这都保证是 ASCII,所以我们可以对原始数字进行操作,最后转换为 String
.
我也摆脱了显式 UUID,而是直接使用 rand
crate,但您可以随意使用。 Wasm 至少应该支持 getrandom
.
pub fn generate_random_guid() -> String {
let random : [u8; 8] = rand::random();
let mut str_bytes = vec![0u8; 16];
const ASCII_ZERO: u8 = '0' as u8;
const ASCII_NINE: u8 = '9' as u8;
const ASCII_NUMBERS_LETTERS_OFFSET: u8 = 'A' as u8 - '9' as u8 - 1;
for i in 0..8 {
let mut leading = random[i] / 16 + ASCII_ZERO;
let mut trailing = random[i] % 16 + ASCII_ZERO;
leading += ((leading > ASCII_NINE) as u8) * ASCII_NUMBERS_LETTERS_OFFSET;
trailing += ((trailing > ASCII_NINE) as u8) * ASCII_NUMBERS_LETTERS_OFFSET;
str_bytes[2 * i] = leading;
str_bytes[2 * i + 1] = trailing;
}
unsafe { String::from_utf8_unchecked(str_bytes) }
}
快速 look 进入 godbolt 表明它被编译成接近最优的 asm,我希望 wasm 也类似。
这是我想用 rust 翻译成 wasm 编译它的原始函数,因为它会使它更快(因为它是我服务器中的热门函数)
export const generateRandomGuid = function (): string {
let guid: string = "0x";
let guidString: string = uuidv4();
const bytes = uuidParse(guidString);
const arrayBytes = new Uint8Array(bytes);
for (let index = 0; index < arrayBytes.length; index++) {
if (guid.length === 18) break;
const byte = arrayBytes[index].toString(16);
if (arrayBytes[index].toString(16).length === 1) {
guid += "0" + byte;
} else {
guid += byte;
}
}
return guid;
};
我在 rust 中是这样翻译的:
use uuid::Uuid;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn generate_random_guid() -> String {
let my_uuid: Uuid = Uuid::new_v4();
let array_bytes = my_uuid.as_bytes();
let mut rand_id: String = String::new();
rand_id.push_str("0x");
for byte in array_bytes {
let formatted_byte: String = format!("{:X}", byte);
if formatted_byte.len() == 1 {
let mut formatted_byte_with_additionnal_zero: String = "0".to_string();
formatted_byte_with_additionnal_zero.push_str(&formatted_byte);
rand_id.push_str(&formatted_byte_with_additionnal_zero);
} else {
rand_id.push_str(&formatted_byte);
}
if rand_id.len() == 18 {
break;
}
}
return rand_id;
}
使用 wasm-pack 和此配置在 wasm 中编译:
[package]
name = "h1emu-core"
version = "0.1.4"
edition = "2018"
[dependencies]
wasm-bindgen = "0.2.45"
uuid = {version = "0.8.2", features = ["v4","wasm-bindgen"], default-features = false }
getrandom = { version = "0.2.3", features = ["js"] }
[lib]
crate-type = ["cdylib"]
[profile.release]
lto = true
opt-level = 3
结果不是想要的,看来js版本比wasm版本快一倍。 所以我问自己是不是我的 rust 代码不好,还是我的配置不好,或者只是我的 wasm 不合适。
代码中的分配数量可以从最坏情况下的 n*2 大大减少到常数 1(忽略 Uuid::new_v4
,我将其视为手头算法外部的常数因子)通过使用 write!
宏和格式说明符:
pub fn generate_random_guid() -> String {
use std::fmt::Write;
let my_uuid: Uuid = Uuid::new_v4();
let array_bytes = my_uuid.as_bytes();
let mut rand_id = String::with_capacity(18);
rand_id.push_str("0x");
for byte in &array_bytes[..8] {
write!(&mut rand_id, "{:02X}", byte).unwrap();
}
rand_id
}
虽然分配可能占运行时间的大部分,但所有格式化机制肯定也无济于事。这都保证是 ASCII,所以我们可以对原始数字进行操作,最后转换为 String
.
我也摆脱了显式 UUID,而是直接使用 rand
crate,但您可以随意使用。 Wasm 至少应该支持 getrandom
.
pub fn generate_random_guid() -> String {
let random : [u8; 8] = rand::random();
let mut str_bytes = vec![0u8; 16];
const ASCII_ZERO: u8 = '0' as u8;
const ASCII_NINE: u8 = '9' as u8;
const ASCII_NUMBERS_LETTERS_OFFSET: u8 = 'A' as u8 - '9' as u8 - 1;
for i in 0..8 {
let mut leading = random[i] / 16 + ASCII_ZERO;
let mut trailing = random[i] % 16 + ASCII_ZERO;
leading += ((leading > ASCII_NINE) as u8) * ASCII_NUMBERS_LETTERS_OFFSET;
trailing += ((trailing > ASCII_NINE) as u8) * ASCII_NUMBERS_LETTERS_OFFSET;
str_bytes[2 * i] = leading;
str_bytes[2 * i + 1] = trailing;
}
unsafe { String::from_utf8_unchecked(str_bytes) }
}
快速 look 进入 godbolt 表明它被编译成接近最优的 asm,我希望 wasm 也类似。