mirror of
https://github.com/bevyengine/bevy.git
synced 2026-05-06 06:06:42 -04:00
a50b65dfa5
# Objective Part of the #23988 and #24058 saga. We attempt to speed up resource access. ## Solution When messing around with #23988 I noticed that changing the storage type mattered a lot for the benchmarks. ## Testing Added benchmarks from #24058 and got the following micro benchmarks compared to main: ``` ecs::resources::get time: [6.3584 ns 6.3840 ns 6.4097 ns] change: [−10.625% −10.075% −9.6652%] (p = 0.00 < 0.05) Performance has improved. Found 9 outliers among 100 measurements (9.00%) 1 (1.00%) low mild 8 (8.00%) high mild ecs::resources::get_mut time: [7.4181 ns 7.4343 ns 7.4515 ns] change: [−39.895% −39.304% −38.809%] (p = 0.00 < 0.05) Performance has improved. Found 7 outliers among 100 measurements (7.00%) 5 (5.00%) high mild 2 (2.00%) high severe ecs::resources::insert_remove time: [89.515 ns 89.654 ns 89.815 ns] change: [−20.163% −16.527% −11.930%] (p = 0.00 < 0.05) Performance has improved. Found 10 outliers among 100 measurements (10.00%) 2 (2.00%) low severe 1 (1.00%) low mild 3 (3.00%) high mild 4 (4.00%) high severe ``` If someone wants to double-check these numbers, I encourage you to do so.
112 lines
3.8 KiB
Rust
112 lines
3.8 KiB
Rust
#![expect(
|
|
dead_code,
|
|
reason = "Many fields are unused/unread as they are just for benchmarking purposes."
|
|
)]
|
|
|
|
use criterion::criterion_main;
|
|
|
|
mod bundles;
|
|
mod change_detection;
|
|
mod components;
|
|
mod empty_archetypes;
|
|
mod entity_cloning;
|
|
mod events;
|
|
mod fragmentation;
|
|
mod iteration;
|
|
mod observers;
|
|
mod param;
|
|
mod resources;
|
|
mod scheduling;
|
|
mod world;
|
|
|
|
criterion_main!(
|
|
bundles::benches,
|
|
change_detection::benches,
|
|
components::benches,
|
|
empty_archetypes::benches,
|
|
entity_cloning::benches,
|
|
events::benches,
|
|
iteration::benches,
|
|
fragmentation::benches,
|
|
observers::benches,
|
|
resources::benches,
|
|
scheduling::benches,
|
|
world::benches,
|
|
param::benches,
|
|
);
|
|
|
|
mod world_builder {
|
|
use bevy_ecs::world::World;
|
|
use rand::{rngs::SmallRng, seq::SliceRandom, SeedableRng};
|
|
|
|
/// This builder generates a "hot"/realistic [`World`].
|
|
///
|
|
/// Using [`World::new`] creates a "cold" world.
|
|
/// That is, the world has a fresh entity allocator, no registered components, and generally no accumulated entropy.
|
|
/// When a cold world is used in a benchmark, much of what is benched is registration and caching costs,
|
|
/// and what is not benched is the cost of the accumulated entropy in world storage, entity allocators, etc.
|
|
///
|
|
/// Use this in benches that are meant to reflect realistic, common, non-startup scenarios (Ex: spawn scenes, query entities, etc).
|
|
/// Prefer [`World::new`] when creating benches for start-up costs (Ex: component registration, table creation time, etc).
|
|
///
|
|
/// Note that this does have a performance cost over [`World::new`], so this should not be used in a benchmark's routine, only in its setup.
|
|
///
|
|
/// Which parts of the world are sped up is highly configurable in the interest of doing the minimal work to warm up a world for a particular benchmark.
|
|
/// (For example, despawn benches wouldn't benefit from warming up world storage.)
|
|
pub struct WorldBuilder {
|
|
world: World,
|
|
rng: SmallRng,
|
|
max_expected_entities: u32,
|
|
}
|
|
|
|
impl WorldBuilder {
|
|
/// Starts the builder.
|
|
pub fn new() -> Self {
|
|
Self {
|
|
world: World::new(),
|
|
rng: SmallRng::seed_from_u64(2039482342342),
|
|
max_expected_entities: 10_000,
|
|
}
|
|
}
|
|
|
|
/// Sets the maximum expected entities that will interact with the world.
|
|
/// By default this is `10_000`.
|
|
pub fn with_max_expected_entities(mut self, max_expected_entities: u32) -> Self {
|
|
self.max_expected_entities = max_expected_entities;
|
|
self
|
|
}
|
|
|
|
/// Warms up the entity allocator to give out arbitrary entity ids instead of sequential ones.
|
|
/// This also pre-allocates room in `Entities`.
|
|
pub fn warm_up_entity_allocator(mut self) -> Self {
|
|
// allocate
|
|
let mut entities = Vec::new();
|
|
entities.reserve_exact(self.max_expected_entities as usize);
|
|
entities.extend(
|
|
self.world
|
|
.entity_allocator()
|
|
.alloc_many(self.max_expected_entities),
|
|
);
|
|
|
|
// Spawn the high index to warm up `Entities`.
|
|
let Some(high_index) = entities.last_mut() else {
|
|
// There were no expected entities.
|
|
return self;
|
|
};
|
|
self.world.spawn_empty_at(*high_index).unwrap();
|
|
*high_index = self.world.try_despawn_no_free(*high_index).unwrap();
|
|
|
|
// free
|
|
entities.shuffle(&mut self.rng);
|
|
self.world.entity_allocator_mut().free_many(&entities);
|
|
|
|
self
|
|
}
|
|
|
|
/// Finishes the builder to get the warmed up world.
|
|
pub fn build(self) -> World {
|
|
self.world
|
|
}
|
|
}
|
|
}
|