Skip to content

Commit

Permalink
perf: Switched to Vec + created benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
Norbiros committed Oct 9, 2024
1 parent 23ac624 commit 254261c
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 30 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ Cargo.lock
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# Flamegraph
flamegraph.svg

# Code editors
.idea/
vscode/
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,15 @@ cesu8 = "1.1.0"
derive_more = { version = "1.0.0", features = ["into", "from"] }
thiserror = "1.0.63"
serde = { version = "1.0.209", optional = true, features = ["derive"] }
rustc-hash = "2.0.0"

[dev-dependencies]
criterion = { version = "0.5.1", features = ["html_reports"] }
flate2 = "1.0.34"

[profile.bench]
debug = true

[[bench]]
name = "read"
harness = false
35 changes: 35 additions & 0 deletions benches/read.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};
use crab_nbt::Nbt;
use std::fs::File;
use std::io::Read;
use bytes::{Bytes, BytesMut};
use flate2::read::GzDecoder;

fn criterion_benchmark(c: &mut Criterion) {
let mut file = File::open("tests/data/complex_player.dat").expect("Failed to open file");
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).expect("Failed to read file");
let mut src = &buffer[..];

let mut src_decoder = GzDecoder::new(&mut src);
let mut input = Vec::new();
if src_decoder.read_to_end(&mut input).is_err() {
input = buffer;
}

let bytes = Bytes::from_iter(input);
let bytes_mut = BytesMut::from(bytes);

let mut group = c.benchmark_group("read");
group.throughput(Throughput::Bytes(bytes_mut.len() as u64));

group.bench_function("read_bigtest_nbt", |b| {
b.iter(|| {
let output = Nbt::read(&mut bytes_mut.clone()).expect("Failed to parse NBT");
black_box(output)
})
});
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
6 changes: 3 additions & 3 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ macro_rules! nbt_inner {
({ $($key:tt : $value:tt),* $(,)? }) => {
$crate::NbtCompound::from_iter({
#[allow(unused_mut)]
let mut map = ::std::collections::HashMap::<::std::string::String, $crate::NbtTag>::new();
$(map.insert($key.into(), nbt_inner!(@value $value));)*
map
let mut values = ::std::vec::Vec::<(::std::string::String, $crate::NbtTag)>::new();
$(values.push(($key.into(), nbt_inner!(@value $value)));)*
values
})
};
(@value $ident:ident) => { $crate::NbtTag::from($ident) };
Expand Down
58 changes: 31 additions & 27 deletions src/nbt/compound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ use bytes::{Buf, BufMut, Bytes, BytesMut};
use crab_nbt::nbt::tag::NbtTag;
use crab_nbt::nbt::utils::{get_nbt_string, END_ID};
use derive_more::Into;
use std::collections::{hash_map::IntoIter, HashMap};
use std::io::{Cursor, Write};
use std::vec::IntoIter;

#[derive(Clone, PartialEq, Debug, Default, Into)]
pub struct NbtCompound {
pub child_tags: HashMap<String, NbtTag>,
pub child_tags: Vec<(String, NbtTag)>,
}

impl NbtCompound {
pub fn new() -> NbtCompound {
NbtCompound {
child_tags: HashMap::new(),
child_tags: Vec::new(),
}
}

pub fn deserialize_content(bytes: &mut impl Buf) -> Result<NbtCompound, Error> {
let mut child_tags = HashMap::new();
let mut child_tags = Vec::new();

while bytes.has_remaining() {
let tag_id = bytes.get_u8();
Expand All @@ -30,7 +30,7 @@ impl NbtCompound {
let name = get_nbt_string(bytes)?;

if let Ok(tag) = NbtTag::deserialize_data(bytes, tag_id) {
child_tags.insert(name, tag);
child_tags.push((name, tag));
} else {
break;
}
Expand Down Expand Up @@ -62,68 +62,72 @@ impl NbtCompound {
}

pub fn put(&mut self, name: String, value: impl Into<NbtTag>) {
self.child_tags.insert(name, value.into());
self.child_tags.push((name, value.into()));
}

pub fn get_byte(&self, name: &str) -> Option<i8> {
self.child_tags.get(name).and_then(|tag| tag.extract_byte())
self.get(name).and_then(|tag| tag.extract_byte())
}

#[inline]
pub fn get(&self, name: &str) -> Option<&NbtTag> {
for (key, value) in &self.child_tags {
if key.as_str() == name {
return Some(value);
}
}
None
}


pub fn get_short(&self, name: &str) -> Option<i16> {
self.child_tags
.get(name)
self.get(name)
.and_then(|tag| tag.extract_short())
}

pub fn get_int(&self, name: &str) -> Option<i32> {
self.child_tags.get(name).and_then(|tag| tag.extract_int())
self.get(name).and_then(|tag| tag.extract_int())
}

pub fn get_long(&self, name: &str) -> Option<i64> {
self.child_tags.get(name).and_then(|tag| tag.extract_long())
self.get(name).and_then(|tag| tag.extract_long())
}

pub fn get_float(&self, name: &str) -> Option<f32> {
self.child_tags
.get(name)
self.get(name)
.and_then(|tag| tag.extract_float())
}

pub fn get_double(&self, name: &str) -> Option<f64> {
self.child_tags
.get(name)
self.get(name)
.and_then(|tag| tag.extract_double())
}

pub fn get_bool(&self, name: &str) -> Option<bool> {
self.child_tags.get(name).and_then(|tag| tag.extract_bool())
self.get(name).and_then(|tag| tag.extract_bool())
}

pub fn get_string(&self, name: &str) -> Option<&String> {
self.child_tags
.get(name)
self.get(name)
.and_then(|tag| tag.extract_string())
}

pub fn get_list(&self, name: &str) -> Option<&Vec<NbtTag>> {
self.child_tags.get(name).and_then(|tag| tag.extract_list())
self.get(name).and_then(|tag| tag.extract_list())
}

pub fn get_compound(&self, name: &str) -> Option<&NbtCompound> {
self.child_tags
.get(name)
self.get(name)
.and_then(|tag| tag.extract_compound())
}

pub fn get_int_array(&self, name: &str) -> Option<&Vec<i32>> {
self.child_tags
.get(name)
self.get(name)
.and_then(|tag| tag.extract_int_array())
}

pub fn get_long_array(&self, name: &str) -> Option<&Vec<i64>> {
self.child_tags
.get(name)
self.get(name)
.and_then(|tag| tag.extract_long_array())
}
}
Expand All @@ -137,14 +141,14 @@ impl From<Nbt> for NbtCompound {
impl FromIterator<(String, NbtTag)> for NbtCompound {
fn from_iter<T: IntoIterator<Item = (String, NbtTag)>>(iter: T) -> Self {
Self {
child_tags: HashMap::from_iter(iter),
child_tags: Vec::from_iter(iter),
}
}
}

impl IntoIterator for NbtCompound {
type Item = (String, NbtTag);
type IntoIter = IntoIter<String, NbtTag>;
type IntoIter = IntoIter<(String, NbtTag)>;

fn into_iter(self) -> Self::IntoIter {
self.child_tags.into_iter()
Expand Down
Binary file added tests/data/complex_player.dat
Binary file not shown.

0 comments on commit 254261c

Please sign in to comment.