From b1098bfbc11fd7e0f3e62f37a7b4989883bf90e2 Mon Sep 17 00:00:00 2001 From: LCW <100464827+wHoIsDReAmer@users.noreply.github.com> Date: Thu, 10 Jul 2025 17:30:14 +0900 Subject: [PATCH] Optimization `ChunkNoiseFunctionComponent` (#1037) * feat(chunk): optimize heap allocations of destiny function * refactor: applied `cargo fmt` and linting error * fix(benches): fix lint errors --- pumpkin-world/Cargo.toml | 4 + pumpkin-world/benches/noise_router.rs | 34 ++++++++ .../noise_router/chunk_density_function.rs | 80 +++++++++++++++---- 3 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 pumpkin-world/benches/noise_router.rs diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 742274ac7..d4cd5c7bb 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -70,3 +70,7 @@ harness = false [[bench]] name = "chunk_gen" harness = false + +[[bench]] +name = "noise_router" +harness = false diff --git a/pumpkin-world/benches/noise_router.rs b/pumpkin-world/benches/noise_router.rs new file mode 100644 index 000000000..005c113bb --- /dev/null +++ b/pumpkin-world/benches/noise_router.rs @@ -0,0 +1,34 @@ +use criterion::{Criterion, criterion_group, criterion_main}; +use pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER; +use pumpkin_world::generation::{ + GlobalRandomConfig, + noise_router::{ + chunk_density_function::ChunkNoiseFunctionBuilderOptions, + chunk_noise_router::ChunkNoiseRouter, proto_noise_router::ProtoNoiseRouters, + }, +}; +use std::hint::black_box; + +fn bench_noise_router_creation(c: &mut Criterion) { + let base_routers = &OVERWORLD_BASE_NOISE_ROUTER; + let random_config = GlobalRandomConfig::new(0, false); + + let proto_routers = ProtoNoiseRouters::generate(base_routers, &random_config); + let proto_noise_router = proto_routers.noise; + + let builder_options = ChunkNoiseFunctionBuilderOptions::new(4, 8, 4, 4, 0, 0, 3); + + // Benchmarking + c.bench_function("noise_router_creation_with_pooling", |b| { + b.iter(|| { + let router = ChunkNoiseRouter::generate( + black_box(&proto_noise_router), + black_box(&builder_options), + ); + black_box(router); + }) + }); +} + +criterion_group!(benches, bench_noise_router_creation); +criterion_main!(benches); diff --git a/pumpkin-world/src/generation/noise_router/chunk_density_function.rs b/pumpkin-world/src/generation/noise_router/chunk_density_function.rs index 6aede4d55..18255d386 100644 --- a/pumpkin-world/src/generation/noise_router/chunk_density_function.rs +++ b/pumpkin-world/src/generation/noise_router/chunk_density_function.rs @@ -1,3 +1,4 @@ +use std::cell::RefCell; use std::mem; use super::{ @@ -9,6 +10,36 @@ use pumpkin_util::math::{lerp, lerp3, vector2::Vector2}; use crate::generation::{biome_coords, positions::chunk_pos}; +thread_local! { + static F64_BUFFER_POOL: RefCell>> = const { + RefCell::new(Vec::new()) + }; +} + +#[inline] +fn get_buffer(len: usize) -> Box<[f64]> { + F64_BUFFER_POOL.with(|pool| { + let mut buffers = pool.borrow_mut(); + if let Some(mut buf) = buffers.pop() { + if buf.len() != len { + buf.resize(len, 0.0); + } else { + buf.fill(0.0); + } + buf.into_boxed_slice() + } else { + vec![0.0; len].into_boxed_slice() + } + }) +} + +#[inline] +fn recycle_buffer(buf: Box<[f64]>) { + F64_BUFFER_POOL.with(|pool| { + pool.borrow_mut().push(Vec::from(buf)); + }); +} + pub struct WrapperData { // Our relative position within the cell pub(crate) cell_x_block_position: usize, @@ -136,18 +167,14 @@ impl DensityInterpolator { // These are all dummy values to be populated when sampling values Self { input_index, - start_buffer: vec![ - 0.0; + start_buffer: get_buffer( (builder_options.vertical_cell_count + 1) - * (builder_options.horizontal_cell_count + 1) - ] - .into_boxed_slice(), - end_buffer: vec![ - 0.0; + * (builder_options.horizontal_cell_count + 1), + ), + end_buffer: get_buffer( (builder_options.vertical_cell_count + 1) - * (builder_options.horizontal_cell_count + 1) - ] - .into_boxed_slice(), + * (builder_options.horizontal_cell_count + 1), + ), first_pass: Default::default(), second_pass: Default::default(), third_pass: Default::default(), @@ -214,6 +241,19 @@ impl DensityInterpolator { } } +impl Drop for DensityInterpolator { + fn drop(&mut self) { + recycle_buffer(std::mem::replace( + &mut self.start_buffer, + Vec::new().into_boxed_slice(), + )); + recycle_buffer(std::mem::replace( + &mut self.end_buffer, + Vec::new().into_boxed_slice(), + )); + } +} + impl MutableChunkNoiseFunctionComponentImpl for DensityInterpolator { fn sample( &mut self, @@ -336,6 +376,15 @@ impl MutableChunkNoiseFunctionComponentImpl for FlatCache { } } +impl Drop for FlatCache { + fn drop(&mut self) { + recycle_buffer(std::mem::replace( + &mut self.cache, + Vec::new().into_boxed_slice(), + )); + } +} + impl FlatCache { pub fn new( input_index: usize, @@ -347,8 +396,7 @@ impl FlatCache { ) -> Self { Self { input_index, - cache: vec![0.0; (horizontal_biome_end + 1) * (horizontal_biome_end + 1)] - .into_boxed_slice(), + cache: get_buffer((horizontal_biome_end + 1) * (horizontal_biome_end + 1)), start_biome_x, start_biome_z, horizontal_biome_end, @@ -589,13 +637,11 @@ impl CellCache { ) -> Self { Self { input_index, - cache: vec![ - 0.0; + cache: get_buffer( build_options.horizontal_cell_block_count * build_options.horizontal_cell_block_count - * build_options.vertical_cell_block_count - ] - .into_boxed_slice(), + * build_options.vertical_cell_block_count, + ), min_value, max_value, }