From 3b802a542fa0583c924e04790f71d850e149cf21 Mon Sep 17 00:00:00 2001 From: Serkyo Date: Tue, 7 Jul 2026 17:17:54 +0200 Subject: [PATCH] perf(server): parallelize chunk generation in load_around --- Cargo.lock | 46 +++++++++++++++++++++++++++++++ crates/server/Cargo.toml | 1 + crates/server/src/world_server.rs | 27 ++++++++++++++---- 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac09b17..f783dff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -335,6 +335,25 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -374,6 +393,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + [[package]] name = "equivalent" version = "1.0.2" @@ -1192,6 +1217,26 @@ dependencies = [ "raw-window-handle", ] +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -1367,6 +1412,7 @@ dependencies = [ name = "server" version = "0.1.0" dependencies = [ + "rayon", "serde_json", "shared", "tracing", diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index 1e8d42f..d82985f 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -9,6 +9,7 @@ authors.workspace = true workspace = true [dependencies] +rayon = "1.12.0" serde_json = "1.0.149" shared = { version = "0.1.0", path = "../shared" } tracing = "0.1.44" diff --git a/crates/server/src/world_server.rs b/crates/server/src/world_server.rs index 5215144..c71ef5d 100644 --- a/crates/server/src/world_server.rs +++ b/crates/server/src/world_server.rs @@ -2,6 +2,7 @@ //! Authoritative chunk storage and generation logic for the server. +use rayon::prelude::*; use shared::{ generator::VoxelGenerator, world::{Chunk, ChunkPos}, @@ -35,9 +36,8 @@ impl ServerWorld { pub fn load_around(&mut self, center_x: f64, center_y: f64, center_z: f64, radius: i32) { let center = ChunkPos::from_world(center_x, center_y, center_z); - // Counts the chunks inside the cylinder that were generated or already resident. - let mut count = 0; - + // Phase 1: collect the positions inside the cylinder that are not yet resident + let mut pending = Vec::new(); for x in center.x - radius..=center.x + radius { for z in center.z - radius..=center.z + radius { for y in center.y - radius / 2..=center.y + radius / 2 { @@ -45,13 +45,28 @@ impl ServerWorld { let dz = z - center.z; if (dx * dx + dz * dz) <= radius * radius { - self.get_chunk(ChunkPos::new(x, y, z)); - count += 1; + let pos = ChunkPos::new(x, y, z); + if !self.chunks.contains_key(&pos) { + pending.push(pos); + } } } } } - tracing::info!(chunks = count, center = ?center, radius, "loaded chunk cylinder"); + // Phase 2: generate the missing chunks in parallel + let generator = &self.generator; + let generated: Vec<(ChunkPos, Chunk)> = pending + .into_par_iter() + .map(|pos| (pos, generator.generate_chunk(pos))) + .collect(); + + // Phase 3: move the finished chunks into the map + let count = generated.len(); + for (pos, chunk) in generated { + self.chunks.insert(pos, chunk); + } + + tracing::info!(chunks = count, center = ?center, radius, "generated chunk cylinder"); } }