笔者在跟随 Rust 重写一切的风气下 基于 Rust 实现了 napi-nanoid
在 node 环境下相比原版 nanoid.js 性能提升 40+%
目录跑分: Linux x64 gnu, Intel® Xeon® Platinum 8370C CPU @ 2.80GHz, Node.js 16.15.1 (runs: 6842925183)
- 挑选技术栈
- 创建 `napi-rs` 工程
- 基于 Rust 实现 nanoid 生成逻辑
- 实现调用方法入口
- 实现最核心的 `format` 逻辑
- 通过 benchmark 对比性能
- 通过 `benny` + `bench` 实现性能测试脚本
- 和原版性能对比
- 和各种随机 id 生成库性能对比
- 发布到 10+ 不同的平台
- 仓库链接
笔者这里选择了 napi-rs 的作为基础实现 napi-nanoid;
选择该技术栈的原因有两方面
-
napi-rs 基于 Rust 生态,在实现 native NAPI 模块时可以更少考虑如 C++ 中内存申请释放的负担。
-
napi-rs 的官方文档非常完整,提供了多个优秀的实践示例。
官网: napi.rs
创建 napi-rs 工程基于 napi-rs/package-template
. ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── __test__ // 单元测试 ├── benchmark // 性能测试 ├── build.rs ├── index.d.ts ├── index.js ├── npm // 多平台入口 ├── package.json ├── rustfmt.toml ├── src // source ├── tsconfig.json └── yarn.lock基于 Rust 实现 nanoid 生成逻辑 实现调用方法入口
调用方法设计了 .nanoid() 和 .non_secure() 高安全和非安全两个方法;
这里两个方法的设计参考了 nanoid.js
#[napi]
pub fn nanoid() -> String {
format(nanoid::rngs::default, &nanoid::alphabet::SAFE, 21)
}
#[napi]
pub fn non_secure() -> String {
format(nanoid::rngs::non_secure, &nanoid::alphabet::SAFE, 21)
}
实现最核心的 format 逻辑
实现过程中用到了宏来提供随机数生成的缓冲区;这里的实现思路也是参考了 nanoid.js
fn format(random: fn(usize) -> Vec, alphabet: &[char], size: usize) -> String { let bytes = &mut POOL.lock().unwrap(); let mask = alphabet.len().next_power_of_two() - 1; let mut pointer = *POOL_OFFSET.lock().unwrap(); let mut id = String::with_capacity(size); while id.len() < size { if pointer == POOL_SIZE { let buf = random(POOL_SIZE); for i in 0..POOL_SIZE { bytes[i] = buf[i]; } pointer = 0; } let byte = bytes[pointer] as usize & mask; if alphabet.len() > byte { id.push(alphabet[byte]); } pointer += 1; } *POOL_OFFSET.lock().unwrap() = pointer; id }
ps: 这里也同 nanoid.js 一样使用了 128 倍长度的缓冲空间
const POOL_SIZE_MULTIPLIER: usize = 128;
const DEFAULT_SIZE: usize = 21;
const POOL_SIZE: usize = DEFAULT_SIZE * POOL_SIZE_MULTIPLIER;
lazy_static! {
static ref POOL: Mutex<[u8; POOL_SIZE]> = Mutex::new([0; POOL_SIZE]);
static ref POOL_OFFSET: Mutex = Mutex::new(POOL_SIZE);
}
通过 benchmark 对比性能
通过 benny + bench 实现性能测试脚本
import benny from 'benny'
import benchmark, { Event } from 'benchmark'
const bench = new benchmark.Suite()
可参考代码: benchmark/bench.ts
和原版性能对比 js-nanoid:
3 392 881 ops/s, ±0.55%
napi-nanoid:
5 113 763 ops/s, ±0.12%
js-nanoid (non-secure):
1 875 245 ops/s, ±0.14%
napi-nanoid (non-secure):
5 237 554 ops/s, ±0.11%
ps: 可以看出 napi-nanoid 版相比 js-nanoid 版本不管在安全还是非安全场景下都至少提升了 40+% 的性能表现。
跑分: (runs: 6842925183)
和各种随机 id 生成库性能对比shortid 24,084 ops/sec cuid 105,736 ops/sec secure-random-string 207,409 ops/sec uuid 840,460 ops/sec js-nanoid (non-secure) 1,826,354 ops/sec js-nanoid (secure) 3,171,036 ops/sec napi-nanoid (secure) 4,837,387 ops/sec napi-nanoid (non-secure) 4,977,971 ops/sec crypto.randomUUID 12,152,367 ops/sec hyperid 16,554,640 ops/sec
跑分: Linux x64 gnu, Intel® Xeon® Platinum 8370C CPU @ 2.80GHz, Node.js 16.15.1
跑分: (runs: 6842925183)
发布到 10+ 不同的平台这里直接通过 napi-rs 官方的脚手架 + github workflow 实现发布到多个平台
| node12 | node14 | node16 | |
|---|---|---|---|
| Windows x64 | ✓ | ✓ | ✓ |
| Windows x32 | ✓ | ✓ | ✓ |
| Windows arm64 | ✓ | ✓ | ✓ |
| macOS x64 | ✓ | ✓ | ✓ |
| macOS arm64 | ✓ | ✓ | ✓ |
| Linux x64 gnu | ✓ | ✓ | ✓ |
| Linux x64 musl | ✓ | ✓ | ✓ |
| Linux arm gnu | ✓ | ✓ | ✓ |
| Linux arm64 gnu | ✓ | ✓ | ✓ |
| Linux arm64 musl | ✓ | ✓ | ✓ |
| Android arm64 | ✓ | ✓ | ✓ |
| Android armv7 | ✓ | ✓ | ✓ |
| FreeBSD x64 | ✓ | ✓ | ✓ |
ps: 使用时无需关注平台 依然仅需引用 napi-nanoid 的 npm 入口包即可
const { nanoid } = require('napi-nanoid');
nanoid() // => AeogKAGjUMX6mqB4sMzWe
仓库链接
Repo: rustq/napi-nanoid
欢迎交流讨论 欢迎大家的 ☆



