Skip to content

Commit

Permalink
Chunk io benchmarking (#601)
Browse files Browse the repository at this point in the history
  • Loading branch information
kralverde authored Mar 2, 2025
1 parent be44ccc commit 7b27da9
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 11 deletions.
2 changes: 1 addition & 1 deletion pumpkin-world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ serde_json5 = { git = "https://github.com/kralverde/serde_json5.git" }

derive-getters = "0.5.0"
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
criterion = { version = "0.5", features = ["html_reports", "async_tokio"] }
temp-dir = "0.1.14"

[[bench]]
Expand Down
75 changes: 73 additions & 2 deletions pumpkin-world/benches/chunk_noise_populate.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use std::{fs, path::PathBuf, sync::Arc};

use criterion::{Criterion, criterion_group, criterion_main};
use pumpkin_util::math::vector2::Vector2;
use pumpkin_world::{
GlobalProtoNoiseRouter, GlobalRandomConfig, NOISE_ROUTER_ASTS, bench_create_and_populate_noise,
chunk::ChunkData, global_path, level::Level,
};
use tokio::sync::RwLock;

fn criterion_benchmark(c: &mut Criterion) {
fn bench_populate_noise(c: &mut Criterion) {
let seed = 0;
let random_config = GlobalRandomConfig::new(seed);
let base_router =
Expand All @@ -14,5 +19,71 @@ fn criterion_benchmark(c: &mut Criterion) {
});
}

criterion_group!(benches, criterion_benchmark);
const MIN_POS: i32 = -4;
const MAX_POS: i32 = 4;

async fn test_reads(root_dir: PathBuf, positions: &[Vector2<i32>]) {
let level = Level::from_root_folder(root_dir);

let rt = tokio::runtime::Handle::current();
let (send, mut recv) = tokio::sync::mpsc::channel(10);
level.fetch_chunks(positions, send, &rt);
while let Some(x) = recv.recv().await {
// Don't compile me away!
let _ = x;
}
}

async fn test_writes(root_dir: PathBuf, chunks: &[(Vector2<i32>, Arc<RwLock<ChunkData>>)]) {
let level = Level::from_root_folder(root_dir);
for (pos, chunk) in chunks {
level.write_chunk((*pos, chunk.clone())).await;
}
}

// Depends on config options from `./config`
fn bench_chunk_io(c: &mut Criterion) {
// System temp dirs are in-memory, so we cant use temp_dir
let root_dir = global_path!("./bench_root");
fs::create_dir(&root_dir).unwrap();

let chunk_positions =
(MIN_POS..=MAX_POS).flat_map(|x| (MIN_POS..=MAX_POS).map(move |z| Vector2::new(x, z)));
let async_handler = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();

println!("Initializing data...");
// Initial writes
let mut chunks = Vec::new();
let mut positions = Vec::new();
async_handler.block_on(async {
let rt = tokio::runtime::Handle::current();
let (send, mut recv) = tokio::sync::mpsc::channel(10);
// Our data dir is empty, so we're generating new chunks here
let level = Level::from_root_folder(root_dir.clone());
level.fetch_chunks(&chunk_positions.collect::<Vec<_>>(), send, &rt);
while let Some((chunk, _)) = recv.recv().await {
let pos = chunk.read().await.position;
chunks.push((pos, chunk));
positions.push(pos);
}
});
println!("Testing with {} chunks", chunks.len());

// These test worst case: no caching done by `Level`
c.bench_function("write chunks", |b| {
b.to_async(&async_handler)
.iter(|| test_writes(root_dir.clone(), &chunks))
});

c.bench_function("read chunks", |b| {
b.to_async(&async_handler)
.iter(|| test_reads(root_dir.clone(), &positions))
});

fs::remove_dir_all(&root_dir).unwrap();
}

criterion_group!(benches, bench_populate_noise, bench_chunk_io);
criterion_main!(benches);
11 changes: 4 additions & 7 deletions pumpkin-world/src/level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,13 +257,10 @@ impl Level {
let chunk_writer = self.chunk_writer.clone();
let level_folder = self.level_folder.clone();

// TODO: Save the join handles to await them when stopping the server
tokio::spawn(async move {
let data = chunk_to_write.1.read().await;
if let Err(error) = chunk_writer.write_chunk(&data, &level_folder, &chunk_to_write.0) {
log::error!("Failed writing Chunk to disk {}", error.to_string());
}
});
let data = chunk_to_write.1.read().await;
if let Err(error) = chunk_writer.write_chunk(&data, &level_folder, &chunk_to_write.0) {
log::error!("Failed writing Chunk to disk {}", error.to_string());
}
}

fn load_chunk_from_save(
Expand Down
2 changes: 1 addition & 1 deletion pumpkin-world/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use generation::proto_chunk::ProtoChunk;
use pumpkin_util::math::vector2::Vector2;

pub mod biome;
Expand Down Expand Up @@ -46,6 +45,7 @@ macro_rules! read_data_from_file {
// TODO: is there a way to do in-file benches?
pub use generation::{
GlobalRandomConfig, noise_router::proto_noise_router::GlobalProtoNoiseRouter,
proto_chunk::ProtoChunk,
};
pub use noise_router::NOISE_ROUTER_ASTS;

Expand Down

0 comments on commit 7b27da9

Please sign in to comment.