Add server-cli bench command for profiling that automates loading a certain area, running for a specific time, and then exiting.

This commit is contained in:
Imbris 2023-06-14 02:45:39 -04:00
parent 6c57d17714
commit aabf67a72a
4 changed files with 68 additions and 14 deletions

View File

@ -82,10 +82,23 @@ pub struct TuiApp {
command: Message,
}
#[derive(Debug, Clone, Copy, Parser)]
pub struct BenchParams {
/// View distance of the loaded area (in chunks)
#[arg(long)]
pub view_distance: u32,
/// Duration to run after loading completes (in seconds).
#[arg(long)]
pub duration: u32,
}
#[derive(Parser)]
pub enum ArgvCommand {
#[command(flatten)]
Shared(SharedCommand),
/// Load an area, run the server for some time, and then exit (useful for
/// profiling).
Bench(BenchParams),
}
#[derive(Parser)]

View File

@ -29,7 +29,7 @@ use server::{persistence::DatabaseSettings, settings::Protocol, Event, Input, Se
use std::{
io,
sync::{atomic::AtomicBool, mpsc, Arc},
time::Duration,
time::{Duration, Instant},
};
use tracing::{info, trace};
@ -117,15 +117,16 @@ fn main() -> io::Result<()> {
sql_log_mode,
};
let mut bench = None;
if let Some(command) = app.command {
return match command {
match command {
ArgvCommand::Shared(SharedCommand::Admin { command }) => {
let login_provider = server::login_provider::LoginProvider::new(
server_settings.auth_server_address,
runtime,
);
match command {
return match command {
Admin::Add { username, role } => {
// FIXME: Currently the UUID can get returned even if the file didn't
// change, so this can't be relied on as an error
@ -139,6 +140,7 @@ fn main() -> io::Result<()> {
&mut editable_settings,
&server_data_dir,
);
Ok(())
},
Admin::Remove { username } => {
// FIXME: Currently the UUID can get returned even if the file didn't
@ -152,9 +154,15 @@ fn main() -> io::Result<()> {
&mut editable_settings,
&server_data_dir,
);
Ok(())
},
}
Ok(())
};
},
ArgvCommand::Bench(params) => {
bench = Some(params);
// If we are trying to benchmark, don't limit the server view distance.
server_settings.max_view_distance = None;
// TODO: add setting to adjust entity spawn density
},
};
}
@ -207,12 +215,28 @@ fn main() -> io::Result<()> {
// Set up an fps clock
let mut clock = Clock::new(Duration::from_secs_f64(1.0 / TPS as f64));
// Wait for a tick so we don't start with a zero dt
if let Some(bench) = bench {
#[cfg(feature = "worldgen")]
server.create_centered_persister(bench.view_distance);
}
let mut bench_exit_time = None;
let mut tick_no = 0u64;
loop {
tick_no += 1;
span!(guard, "work");
if let Some(bench) = bench {
if let Some(t) = bench_exit_time {
if Instant::now() > t {
break;
}
} else if tick_no != 0 && !server.chunks_pending() {
println!("Chunk loading complete");
bench_exit_time = Some(Instant::now() + Duration::from_secs(bench.duration.into()));
}
};
tick_no += 1;
// Terminate the server if instructed to do so by the shutdown coordinator
if shutdown_coordinator.check(&mut server, &settings) {
break;

View File

@ -1442,6 +1442,15 @@ impl Server {
.build();
}
/// Used by benchmarking code.
pub fn chunks_pending(&mut self) -> bool {
self.state_mut()
.mut_resource::<ChunkGenerator>()
.pending_chunks()
.next()
.is_some()
}
/// Sets the SQL log mode at runtime
pub fn set_sql_log_mode(&mut self, sql_log_mode: SqlLogMode) {
// Unwrap is safe here because we only perform a variable assignment with the

View File

@ -1,4 +1,5 @@
use crate::{
chunk_generator::ChunkGenerator,
metrics::{EcsSystemMetrics, JobMetrics, PhysicsMetrics, TickMetrics},
HwStats, Tick, TickStart,
};
@ -12,11 +13,12 @@ use std::time::Instant;
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Option<Entities<'a>>,
Entities<'a>,
ReadExpect<'a, HwStats>,
ReadExpect<'a, Tick>,
ReadExpect<'a, TimeOfDay>,
ReadExpect<'a, TickStart>,
ReadExpect<'a, ChunkGenerator>,
Option<Read<'a, TerrainGrid>>,
Read<'a, SysMetrics>,
Read<'a, common_ecs::PhysicsMetrics>,
@ -39,6 +41,7 @@ impl<'a> System<'a> for Sys {
tick,
time_of_day,
tick_start,
chunk_generator,
terrain,
sys_metrics,
phys_metrics,
@ -89,7 +92,7 @@ impl<'a> System<'a> for Sys {
// Report other info
export_tick.time_of_day.set(time_of_day.0);
if tick.0.rem_euclid(100) == 0 {
if let Some(terrain) = terrain {
if let Some(terrain) = terrain.as_ref() {
let mut chonk_cnt = 0;
let mut group_cnt = 0;
let chunk_cnt = terrain.iter().fold(0, |a, (_, c)| {
@ -102,11 +105,16 @@ impl<'a> System<'a> for Sys {
export_tick.chunk_groups_count.set(group_cnt as i64);
}
if let Some(entities) = entities {
let entity_count = entities.join().count();
export_tick.entity_count.set(entity_count as i64);
common_base::plot!("entity count", entity_count as f64);
}
let entity_count = entities.join().count();
export_tick.entity_count.set(entity_count as i64);
}
common_base::plot!("entity count", entities.join().count() as f64);
common_base::plot!(
"pending chunks",
chunk_generator.pending_chunks().count() as f64
);
if let Some(terrain) = terrain.as_ref() {
common_base::plot!("chunk count", terrain.iter().count() as f64);
}
//detailed physics metrics