Add more profiling spans to Server startup related things in order to

visualize where time is being spent. Make `Lod::from_world` use the same
rayon pool as other things during the server startup. Move parallel
iteration up out of structure_gen.par_iter in order to slightly optimize
Lod::from_world (saves a few hundred milliseconds on my machine)
This commit is contained in:
Imbris 2023-04-18 21:18:12 -04:00
parent 92a42ced18
commit c6f5e8dac2
10 changed files with 61 additions and 39 deletions

View File

@ -3,6 +3,7 @@ use super::{
vec2_as_uniform_idx, TerrainChunkSize, NEIGHBOR_DELTA, TERRAIN_CHUNK_BLOCKS_LG, vec2_as_uniform_idx, TerrainChunkSize, NEIGHBOR_DELTA, TERRAIN_CHUNK_BLOCKS_LG,
}; };
use crate::vol::RectVolSize; use crate::vol::RectVolSize;
use common_base::prof_span;
use core::{f32, f64, iter, ops::RangeInclusive}; use core::{f32, f64, iter, ops::RangeInclusive};
use vek::*; use vek::*;
@ -451,6 +452,7 @@ impl<'a> MapConfig<'a> {
sample_wpos: impl Fn(Vec2<i32>) -> f32, sample_wpos: impl Fn(Vec2<i32>) -> f32,
mut write_pixel: impl FnMut(Vec2<usize>, (u8, u8, u8, u8)), mut write_pixel: impl FnMut(Vec2<usize>, (u8, u8, u8, u8)),
) -> MapDebug { ) -> MapDebug {
prof_span!("MapConfig::generate");
let MapConfig { let MapConfig {
map_size_lg, map_size_lg,
dimensions, dimensions,

View File

@ -24,7 +24,7 @@ use common::{
vol::{ReadVol, WriteVol}, vol::{ReadVol, WriteVol},
weather::{Weather, WeatherGrid}, weather::{Weather, WeatherGrid},
}; };
use common_base::span; use common_base::{prof_span, span};
use common_ecs::{PhysicsMetrics, SysMetrics}; use common_ecs::{PhysicsMetrics, SysMetrics};
use common_net::sync::{interpolation as sync_interp, WorldSyncExt}; use common_net::sync::{interpolation as sync_interp, WorldSyncExt};
use core::{convert::identity, time::Duration}; use core::{convert::identity, time::Duration};
@ -179,6 +179,7 @@ impl State {
map_size_lg: MapSizeLg, map_size_lg: MapSizeLg,
default_chunk: Arc<TerrainChunk>, default_chunk: Arc<TerrainChunk>,
) -> specs::World { ) -> specs::World {
prof_span!("State::setup_ecs_world");
let mut ecs = specs::World::new(); let mut ecs = specs::World::new();
// Uids for sync // Uids for sync
ecs.register_sync_marker(); ecs.register_sync_marker();

View File

@ -284,6 +284,8 @@ impl Server {
default_chunk: Arc::new(world.generate_oob_chunk()), default_chunk: Arc::new(world.generate_oob_chunk()),
}; };
let lod = lod::Lod::from_world(&world, index.as_index_ref(), &pools);
let mut state = State::server( let mut state = State::server(
pools, pools,
world.sim().map_size_lg(), world.sim().map_size_lg(),
@ -455,7 +457,7 @@ impl Server {
state.ecs_mut().insert(Arc::clone(&world)); state.ecs_mut().insert(Arc::clone(&world));
state state
.ecs_mut() .ecs_mut()
.insert(lod::Lod::from_world(&world, index.as_index_ref())); .insert(lod);
state.ecs_mut().insert(index.clone()); state.ecs_mut().insert(index.clone());
// Set starting time for the server. // Set starting time for the server.

View File

@ -17,19 +17,23 @@ pub struct Lod {
impl Lod { impl Lod {
#[cfg(feature = "worldgen")] #[cfg(feature = "worldgen")]
pub fn from_world(world: &World, index: IndexRef) -> Self { pub fn from_world(world: &World, index: IndexRef, threadpool: &rayon::ThreadPool) -> Self {
let mut zones = HashMap::new(); common_base::prof_span!("Lod::from_world");
threadpool.install(|| {
let zone_sz = (world.sim().get_size() + lod::ZONE_SIZE - 1) / lod::ZONE_SIZE;
let zone_sz = (world.sim().get_size() + lod::ZONE_SIZE - 1) / lod::ZONE_SIZE; use rayon::prelude::*;
let zones = (0..zone_sz.x)
.into_par_iter()
.flat_map(|i| (0..zone_sz.y).into_par_iter().map(move |j| (i, j)))
.map(|(i, j)| {
let zone_pos = Vec2::new(i, j).map(|e| e as i32);
(zone_pos, world.get_lod_zone(zone_pos, index))
})
.collect();
for i in 0..zone_sz.x { Self { zones }
for j in 0..zone_sz.y { })
let zone_pos = Vec2::new(i, j).map(|e| e as i32);
zones.insert(zone_pos, world.get_lod_zone(zone_pos, index));
}
}
Self { zones }
} }
#[cfg(not(feature = "worldgen"))] #[cfg(not(feature = "worldgen"))]

View File

@ -275,6 +275,7 @@ impl Civs {
//=== old economy is gone //=== old economy is gone
// Flatten ground around sites // Flatten ground around sites
prof_span!(guard, "Flatten ground around sites");
for site in this.sites.values() { for site in this.sites.values() {
let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32); let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32);
@ -339,8 +340,10 @@ impl Civs {
} }
} }
} }
drop(guard);
// Place sites in world // Place sites in world
prof_span!(guard, "Place sites in world");
let mut cnt = 0; let mut cnt = 0;
for sim_site in this.sites.values_mut() { for sim_site in this.sites.values_mut() {
cnt += 1; cnt += 1;
@ -436,6 +439,7 @@ impl Civs {
} }
debug!(?sim_site.center, "Placed site at location"); debug!(?sim_site.center, "Placed site at location");
} }
drop(guard);
info!(?cnt, "all sites placed"); info!(?cnt, "all sites placed");
//this.display_info(); //this.display_info();
@ -465,23 +469,25 @@ impl Civs {
} }
} }
// TODO: this looks optimizable
// collect natural resources // collect natural resources
prof_span!(guard, "collect natural resources");
let sites = &mut index.sites; let sites = &mut index.sites;
(0..ctx.sim.map_size_lg().chunks_len()) (0..ctx.sim.map_size_lg().chunks_len()).for_each(|posi| {
.into_iter() let chpos = uniform_idx_as_vec2(ctx.sim.map_size_lg(), posi);
.for_each(|posi| { let wpos = chpos.map(|e| e as i64) * TerrainChunkSize::RECT_SIZE.map(|e| e as i64);
let chpos = uniform_idx_as_vec2(ctx.sim.map_size_lg(), posi); let closest_site = (*sites)
let wpos = chpos.map(|e| e as i64) * TerrainChunkSize::RECT_SIZE.map(|e| e as i64); .iter_mut()
let closest_site = (*sites) .filter(|s| !matches!(s.1.kind, crate::site::SiteKind::Dungeon(_)))
.iter_mut() .min_by_key(|(_id, s)| s.get_origin().map(|e| e as i64).distance_squared(wpos));
.filter(|s| !matches!(s.1.kind, crate::site::SiteKind::Dungeon(_))) if let Some((_id, s)) = closest_site {
.min_by_key(|(_id, s)| s.get_origin().map(|e| e as i64).distance_squared(wpos)); let distance_squared = s.get_origin().map(|e| e as i64).distance_squared(wpos);
if let Some((_id, s)) = closest_site { s.economy
let distance_squared = s.get_origin().map(|e| e as i64).distance_squared(wpos); .add_chunk(ctx.sim.get(chpos).unwrap(), distance_squared);
s.economy }
.add_chunk(ctx.sim.get(chpos).unwrap(), distance_squared); });
} drop(guard);
});
sites sites
.iter_mut() .iter_mut()
.for_each(|(_, s)| s.economy.cache_economy()); .for_each(|(_, s)| s.economy.cache_economy());

View File

@ -60,7 +60,6 @@ use common_net::msg::{world_msg, WorldMapMsg};
use enum_map::EnumMap; use enum_map::EnumMap;
use rand::{prelude::*, Rng}; use rand::{prelude::*, Rng};
use rand_chacha::ChaCha8Rng; use rand_chacha::ChaCha8Rng;
use rayon::iter::ParallelIterator;
use serde::Deserialize; use serde::Deserialize;
use std::time::Duration; use std::time::Duration;
use vek::*; use vek::*;
@ -138,6 +137,7 @@ impl World {
} }
pub fn get_map_data(&self, index: IndexRef, threadpool: &rayon::ThreadPool) -> WorldMapMsg { pub fn get_map_data(&self, index: IndexRef, threadpool: &rayon::ThreadPool) -> WorldMapMsg {
prof_span!("World::get_map_data");
threadpool.install(|| { threadpool.install(|| {
// we need these numbers to create unique ids for cave ends // we need these numbers to create unique ids for cave ends
let num_sites = self.civs().sites().count() as u64; let num_sites = self.civs().sites().count() as u64;
@ -564,6 +564,7 @@ impl World {
let mut objects = Vec::new(); let mut objects = Vec::new();
// Add trees // Add trees
prof_span!(guard, "add trees");
objects.append( objects.append(
&mut self &mut self
.sim() .sim()
@ -602,6 +603,7 @@ impl World {
}) })
.collect(), .collect(),
); );
drop(guard);
// Add buildings // Add buildings
objects.extend( objects.extend(

View File

@ -51,6 +51,7 @@ use common::{
}, },
vol::RectVolSize, vol::RectVolSize,
}; };
use common_base::prof_span;
use common_net::msg::WorldMapMsg; use common_net::msg::WorldMapMsg;
use noise::{ use noise::{
BasicMulti, Billow, Fbm, HybridMulti, MultiFractal, NoiseFn, RangeFunction, RidgedMulti, BasicMulti, Billow, Fbm, HybridMulti, MultiFractal, NoiseFn, RangeFunction, RidgedMulti,
@ -668,6 +669,7 @@ pub struct WorldSim {
impl WorldSim { impl WorldSim {
pub fn generate(seed: u32, opts: WorldOpts, threadpool: &rayon::ThreadPool) -> Self { pub fn generate(seed: u32, opts: WorldOpts, threadpool: &rayon::ThreadPool) -> Self {
prof_span!("WorldSim::generate");
let calendar = opts.calendar; // separate lifetime of elements let calendar = opts.calendar; // separate lifetime of elements
let world_file = opts.world_file; let world_file = opts.world_file;
@ -1586,6 +1588,7 @@ impl WorldSim {
/// Draw a map of the world based on chunk information. Returns a buffer of /// Draw a map of the world based on chunk information. Returns a buffer of
/// u32s. /// u32s.
pub fn get_map(&self, index: IndexRef, calendar: Option<&Calendar>) -> WorldMapMsg { pub fn get_map(&self, index: IndexRef, calendar: Option<&Calendar>) -> WorldMapMsg {
prof_span!("WorldSim::get_map");
let mut map_config = MapConfig::orthographic( let mut map_config = MapConfig::orthographic(
self.map_size_lg(), self.map_size_lg(),
core::ops::RangeInclusive::new(CONFIG.sea_level, CONFIG.sea_level + self.max_height), core::ops::RangeInclusive::new(CONFIG.sea_level, CONFIG.sea_level + self.max_height),
@ -1603,6 +1606,7 @@ impl WorldSim {
}; };
let samples_data = { let samples_data = {
prof_span!("samples data");
let column_sample = ColumnGen::new(self); let column_sample = ColumnGen::new(self);
(0..self.map_size_lg().chunks_len()) (0..self.map_size_lg().chunks_len())
.into_par_iter() .into_par_iter()
@ -2293,10 +2297,10 @@ impl WorldSim {
&self, &self,
wpos_min: Vec2<i32>, wpos_min: Vec2<i32>,
wpos_max: Vec2<i32>, wpos_max: Vec2<i32>,
) -> impl ParallelIterator<Item = TreeAttr> + '_ { ) -> impl Iterator<Item = TreeAttr> + '_ {
self.gen_ctx self.gen_ctx
.structure_gen .structure_gen
.par_iter(wpos_min, wpos_max) .iter(wpos_min, wpos_max)
.filter_map(move |(wpos, seed)| { .filter_map(move |(wpos, seed)| {
let lottery = self.make_forest_lottery(wpos); let lottery = self.make_forest_lottery(wpos);
Some(TreeAttr { Some(TreeAttr {

View File

@ -3,6 +3,7 @@ use common::{
terrain::{neighbors, uniform_idx_as_vec2, vec2_as_uniform_idx, MapSizeLg, TerrainChunkSize}, terrain::{neighbors, uniform_idx_as_vec2, vec2_as_uniform_idx, MapSizeLg, TerrainChunkSize},
vol::RectVolSize, vol::RectVolSize,
}; };
use common_base::prof_span;
use noise::{MultiFractal, NoiseFn, Perlin, Seedable}; use noise::{MultiFractal, NoiseFn, Perlin, Seedable};
use num::Float; use num::Float;
use rayon::prelude::*; use rayon::prelude::*;
@ -374,6 +375,7 @@ pub fn get_horizon_map<F: Float + Sync, A: Send, H: Send>(
to_angle: impl Fn(F) -> A + Sync, to_angle: impl Fn(F) -> A + Sync,
to_height: impl Fn(F) -> H + Sync, to_height: impl Fn(F) -> H + Sync,
) -> Result<[HorizonMap<A, H>; 2], ()> { ) -> Result<[HorizonMap<A, H>; 2], ()> {
prof_span!("get_horizon_map");
if maxh < minh { if maxh < minh {
// maxh must be greater than minh // maxh must be greater than minh
return Err(()); return Err(());

View File

@ -1,3 +1,7 @@
use crate::{sim::WorldSim, site::economy::simulate_economy, Index}; use crate::{sim::WorldSim, site::economy::simulate_economy, Index};
use common_base::prof_span;
pub fn simulate(index: &mut Index, _world: &mut WorldSim) { simulate_economy(index); } pub fn simulate(index: &mut Index, _world: &mut WorldSim) {
prof_span!("sim2::simulate");
simulate_economy(index);
}

View File

@ -1,5 +1,4 @@
use super::{RandomField, Sampler}; use super::{RandomField, Sampler};
use rayon::prelude::*;
use vek::*; use vek::*;
#[derive(Clone)] #[derive(Clone)]
@ -69,11 +68,7 @@ impl StructureGen2d {
/// Note: Generates all possible closest samples for elements in the range /// Note: Generates all possible closest samples for elements in the range
/// of min to max, *exclusive.* /// of min to max, *exclusive.*
pub fn par_iter( pub fn iter(&self, min: Vec2<i32>, max: Vec2<i32>) -> impl Iterator<Item = StructureField> {
&self,
min: Vec2<i32>,
max: Vec2<i32>,
) -> impl ParallelIterator<Item = StructureField> {
let freq = self.freq; let freq = self.freq;
let spread = self.spread; let spread = self.spread;
let spread_mul = Self::spread_mul(spread); let spread_mul = Self::spread_mul(spread);
@ -102,7 +97,7 @@ impl StructureGen2d {
let x_field = self.x_field; let x_field = self.x_field;
let y_field = self.y_field; let y_field = self.y_field;
let seed_field = self.seed_field; let seed_field = self.seed_field;
(0..len).into_par_iter().map(move |xy| { (0..len).map(move |xy| {
let index = min_index + Vec2::new((xy % xlen as u64) as i32, (xy / xlen as u64) as i32); let index = min_index + Vec2::new((xy % xlen as u64) as i32, (xy / xlen as u64) as i32);
Self::index_to_sample_internal( Self::index_to_sample_internal(
freq, freq,