diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 586393c9e..e6b88528d 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -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]] diff --git a/pumpkin-world/benches/chunk_noise_populate.rs b/pumpkin-world/benches/chunk_noise_populate.rs index 46d4d11e2..ef09c8e41 100644 --- a/pumpkin-world/benches/chunk_noise_populate.rs +++ b/pumpkin-world/benches/chunk_noise_populate.rs @@ -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 = @@ -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]) { + 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, Arc>)]) { + 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::>(), 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); diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 728fc9ad9..c69fbc9f7 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -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( diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index d8e58e971..39bf36f01 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -1,4 +1,3 @@ -use generation::proto_chunk::ProtoChunk; use pumpkin_util::math::vector2::Vector2; pub mod biome; @@ -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;