13个版本 (5个破坏性版本)
0.6.0 | 2024年7月5日 |
---|---|
0.5.2 | 2024年5月14日 |
0.4.2 | 2024年3月15日 |
0.4.0 | 2024年1月25日 |
0.1.0 | 2023年8月30日 |
#23 in 游戏
每月下载量429
用于 17 个crate (直接使用11个)
180KB
5K SLoC
simdnbt
Simdnbt是一个非常快速的NBT序列化和反序列化器。
它最初是作为一个玩笑而制作的,但最终变成了一个太好的玩笑,所以现在它实际上是一件事。
用法
cargo add simdnbt
反序列化
对于反序列化,你可能想使用 simdnbt::borrow::read 或 simdnbt::owned::read。区别在于“借用”变体需要你保留对原始缓冲区的引用,但速度要快得多。
use std::borrow::Cow;
use std::io::Cursor;
fn example(item_bytes: &[u8]) {
let nbt = simdnbt::borrow::read(&mut Cursor::new(item_bytes))
.unwrap()
.unwrap();
let skyblock_id: Cow<str> = nbt
.list("i")
.and_then(|i| i.compounds())
.and_then(|i| i.first())
.and_then(|i| i.compound("tag"))
.and_then(|tag| tag.compound("ExtraAttributes"))
.and_then(|ea| ea.string("id"))
.map(|id| id.to_string_lossy())
.unwrap_or_default();
}
序列化
use simdnbt::owned::{BaseNbt, Nbt, NbtCompound, NbtTag};
let nbt = Nbt::Some(BaseNbt::new(
"",
NbtCompound::from_values(vec![
("key".into(), NbtTag::String("value".into())),
]),
));
let mut buffer = Vec::new();
nbt.write(&mut buffer);
性能指南
如果可能,请使用 Nbt
的借用变体,并避免不必要的分配(例如,如果可能,将字符串保留为 Cow<str>
)。
你可以做的最重要的优化之一是切换到像 mimalloc 这样的分配器(在我的机器上大约快20%)。在运行你的代码时设置 RUSTFLAGS='-C target-cpu=native'
也可能有所帮助。
实现细节
Simdnbt目前使用SIMD指令做两件事
- 交换int数组的字节序
- 检查字符串是否是纯ASCII,以便更快地将mutf8转换为utf8
Simdnbt 作弊 为了变得如此之快而采取了一些捷径
- 它需要原始数据的引用(以避免克隆)
- 在解码时它不验证/解码mutf-8字符串
基准测试
Simdnbt可能是目前存在的最快的NBT解码器。
以下是对Simdnbt与其他几个最快的NBT解码crate进行的基准比较,解码complex_player.dat
库 | 吞吐量 |
---|---|
simdnbt::borrow | 3.9493 GiB/s |
simdnbt::owned | 825.59 MiB/s |
shen_nbt5 | 606.68 MiB/s |
graphite_binary | 363.94 MiB/s |
azalea_nbt | 330.46 MiB/s |
valence_nbt | 279.58 MiB/s |
hematite_nbt | 180.22 MiB/s |
fastnbt | 162.92 MiB/s |
对于写入complex_player.dat
库 | 吞吐量 |
---|---|
simdnbt::owned | 2.5033 GiB/s |
azalea_nbt | 2.4152 GiB/s |
simdnbt::borrow | 2.1317 GiB/s |
graphite_binary | 1.8804 GiB/s |
上表来自本仓库中的比较基准。请注意,这个基准并不完全公平,因为simdnbt::borrow
直到使用前不会完全解码一些东西,比如字符串和整数数组。另外,如果你运行自己的基准测试,你将得到不同的数字,但速度应该相对相同。
依赖项
~0.8–1.4MB
~29K SLoC