mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
@ -82,10 +82,23 @@ pub struct TuiApp {
|
|||||||
command: Message,
|
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)]
|
#[derive(Parser)]
|
||||||
pub enum ArgvCommand {
|
pub enum ArgvCommand {
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
Shared(SharedCommand),
|
Shared(SharedCommand),
|
||||||
|
/// Load an area, run the server for some time, and then exit (useful for
|
||||||
|
/// profiling).
|
||||||
|
Bench(BenchParams),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
@ -29,7 +29,7 @@ use server::{persistence::DatabaseSettings, settings::Protocol, Event, Input, Se
|
|||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
sync::{atomic::AtomicBool, mpsc, Arc},
|
sync::{atomic::AtomicBool, mpsc, Arc},
|
||||||
time::Duration,
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use tracing::{info, trace};
|
use tracing::{info, trace};
|
||||||
|
|
||||||
@ -117,15 +117,16 @@ fn main() -> io::Result<()> {
|
|||||||
sql_log_mode,
|
sql_log_mode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut bench = None;
|
||||||
if let Some(command) = app.command {
|
if let Some(command) = app.command {
|
||||||
return match command {
|
match command {
|
||||||
ArgvCommand::Shared(SharedCommand::Admin { command }) => {
|
ArgvCommand::Shared(SharedCommand::Admin { command }) => {
|
||||||
let login_provider = server::login_provider::LoginProvider::new(
|
let login_provider = server::login_provider::LoginProvider::new(
|
||||||
server_settings.auth_server_address,
|
server_settings.auth_server_address,
|
||||||
runtime,
|
runtime,
|
||||||
);
|
);
|
||||||
|
|
||||||
match command {
|
return match command {
|
||||||
Admin::Add { username, role } => {
|
Admin::Add { username, role } => {
|
||||||
// FIXME: Currently the UUID can get returned even if the file didn't
|
// FIXME: Currently the UUID can get returned even if the file didn't
|
||||||
// change, so this can't be relied on as an error
|
// change, so this can't be relied on as an error
|
||||||
@ -139,6 +140,7 @@ fn main() -> io::Result<()> {
|
|||||||
&mut editable_settings,
|
&mut editable_settings,
|
||||||
&server_data_dir,
|
&server_data_dir,
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
},
|
},
|
||||||
Admin::Remove { username } => {
|
Admin::Remove { username } => {
|
||||||
// FIXME: Currently the UUID can get returned even if the file didn't
|
// FIXME: Currently the UUID can get returned even if the file didn't
|
||||||
@ -152,11 +154,17 @@ fn main() -> io::Result<()> {
|
|||||||
&mut editable_settings,
|
&mut editable_settings,
|
||||||
&server_data_dir,
|
&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
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panic hook to ensure that console mode is set back correctly if in non-basic
|
// Panic hook to ensure that console mode is set back correctly if in non-basic
|
||||||
@ -207,12 +215,28 @@ fn main() -> io::Result<()> {
|
|||||||
|
|
||||||
// Set up an fps clock
|
// Set up an fps clock
|
||||||
let mut clock = Clock::new(Duration::from_secs_f64(1.0 / TPS as f64));
|
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;
|
let mut tick_no = 0u64;
|
||||||
loop {
|
loop {
|
||||||
tick_no += 1;
|
|
||||||
span!(guard, "work");
|
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
|
// Terminate the server if instructed to do so by the shutdown coordinator
|
||||||
if shutdown_coordinator.check(&mut server, &settings) {
|
if shutdown_coordinator.check(&mut server, &settings) {
|
||||||
break;
|
break;
|
||||||
|
@ -1442,6 +1442,15 @@ impl Server {
|
|||||||
.build();
|
.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
|
/// Sets the SQL log mode at runtime
|
||||||
pub fn set_sql_log_mode(&mut self, sql_log_mode: SqlLogMode) {
|
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
|
// Unwrap is safe here because we only perform a variable assignment with the
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
chunk_generator::ChunkGenerator,
|
||||||
metrics::{EcsSystemMetrics, JobMetrics, PhysicsMetrics, TickMetrics},
|
metrics::{EcsSystemMetrics, JobMetrics, PhysicsMetrics, TickMetrics},
|
||||||
HwStats, Tick, TickStart,
|
HwStats, Tick, TickStart,
|
||||||
};
|
};
|
||||||
@ -12,11 +13,12 @@ use std::time::Instant;
|
|||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Option<Entities<'a>>,
|
Entities<'a>,
|
||||||
ReadExpect<'a, HwStats>,
|
ReadExpect<'a, HwStats>,
|
||||||
ReadExpect<'a, Tick>,
|
ReadExpect<'a, Tick>,
|
||||||
ReadExpect<'a, TimeOfDay>,
|
ReadExpect<'a, TimeOfDay>,
|
||||||
ReadExpect<'a, TickStart>,
|
ReadExpect<'a, TickStart>,
|
||||||
|
ReadExpect<'a, ChunkGenerator>,
|
||||||
Option<Read<'a, TerrainGrid>>,
|
Option<Read<'a, TerrainGrid>>,
|
||||||
Read<'a, SysMetrics>,
|
Read<'a, SysMetrics>,
|
||||||
Read<'a, common_ecs::PhysicsMetrics>,
|
Read<'a, common_ecs::PhysicsMetrics>,
|
||||||
@ -39,6 +41,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
tick,
|
tick,
|
||||||
time_of_day,
|
time_of_day,
|
||||||
tick_start,
|
tick_start,
|
||||||
|
chunk_generator,
|
||||||
terrain,
|
terrain,
|
||||||
sys_metrics,
|
sys_metrics,
|
||||||
phys_metrics,
|
phys_metrics,
|
||||||
@ -89,7 +92,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Report other info
|
// Report other info
|
||||||
export_tick.time_of_day.set(time_of_day.0);
|
export_tick.time_of_day.set(time_of_day.0);
|
||||||
if tick.0.rem_euclid(100) == 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 chonk_cnt = 0;
|
||||||
let mut group_cnt = 0;
|
let mut group_cnt = 0;
|
||||||
let chunk_cnt = terrain.iter().fold(0, |a, (_, c)| {
|
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);
|
export_tick.chunk_groups_count.set(group_cnt as i64);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(entities) = entities {
|
|
||||||
let entity_count = entities.join().count();
|
let entity_count = entities.join().count();
|
||||||
export_tick.entity_count.set(entity_count as i64);
|
export_tick.entity_count.set(entity_count as i64);
|
||||||
common_base::plot!("entity count", entity_count as f64);
|
|
||||||
}
|
}
|
||||||
|
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
|
//detailed physics metrics
|
||||||
|
Reference in New Issue
Block a user