我编译成 wasm 的 Rust 代码比 js 慢,我做错了什么?

问题描述

这是我想在 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(忽略 extern "C",我认为它是手头算法外部的常数因素) Uuid::new_v4 宏和格式说明符的使用:

write!
,

虽然分配可能占运行时间的大部分,但所有格式化机制肯定也无济于事。这都保证是 ASCII,所以我们可以对原始数字进行操作,最后转换为 String

我还去掉了显式的 UUID,而是直接使用 rand 板条箱,但您可以随意这样做。 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) }
}

对 Godbolt 的快速 look 显示这被编译成接近最佳的 asm,我希望 wasm 也类似。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...