笔者在跟随
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
的作为基础实现 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
调用方法设计了 .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<u8>, 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<usize> = Mutex::new(POOL_SIZE);
}
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)
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)
这里直接通过 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
欢迎交流讨论 欢迎大家的 ☆