Crates.io | simdnbt |
lib.rs | simdnbt |
version | 0.6.0 |
source | src |
created_at | 2023-08-30 04:04:33.826804 |
updated_at | 2024-07-05 04:40:41.064655 |
description | an unnecessarily fast nbt decoder |
homepage | |
repository | https://github.com/azalea-rs/simdnbt |
max_upload_size | |
id | 958698 |
size | 257,012 |
Simdnbt is a very fast NBT serializer and deserializer.
It was originally made as a joke but it ended up being too good of a joke so it's actually a thing now.
cargo add simdnbt
For deserializing, you'll likely want either simdnbt::borrow::read or simdnbt::owned::read. The difference is that the "borrow" variant requires you to keep a reference to the original buffer, but is significantly faster.
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);
Use the borrow variant of Nbt
if possible, and avoid allocating unnecessarily (for example, keep strings as Cow<str>
if you can).
The most significant and simple optimization you can do is switching to an allocator like mimalloc (it's ~20% faster on my machine). Setting RUSTFLAGS='-C target-cpu=native'
when running your code may also help a little bit.
Simdnbt currently makes use of SIMD instructions for two things:
Simdnbt cheats takes some shortcuts to be this fast:
Simdnbt is likely the fastest NBT decoder currently in existence.
Here's a benchmark comparing Simdnbt against a few of the other fastest NBT crates for decoding complex_player.dat
:
Library | Throughput |
---|---|
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 |
And for writing complex_player.dat
:
Library | Throughput |
---|---|
simdnbt::owned | 2.5033 GiB/s |
azalea_nbt | 2.4152 GiB/s |
simdnbt::borrow | 2.1317 GiB/s |
graphite_binary | 1.8804 GiB/s |
The tables above were made from the compare benchmark in this repo.
Note that the benchmark is somewhat unfair, since simdnbt::borrow
doesn't fully decode some things like strings and integer arrays until they're used.
Also keep in mind that if you run your own benchmark you'll get different numbers, but the speeds should be about the same relative to each other.