Store resources on sparse sets (#24077)

# 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.
This commit is contained in:
Trashtalk217
2026-05-05 00:13:34 +02:00
committed by Carter Anderson
parent 0d6131008c
commit a50b65dfa5
3 changed files with 73 additions and 1 deletions
+2
View File
@@ -15,6 +15,7 @@ mod fragmentation;
mod iteration;
mod observers;
mod param;
mod resources;
mod scheduling;
mod world;
@@ -28,6 +29,7 @@ criterion_main!(
iteration::benches,
fragmentation::benches,
observers::benches,
resources::benches,
scheduling::benches,
world::benches,
param::benches,
+69
View File
@@ -0,0 +1,69 @@
use std::{alloc::Layout, hint::black_box, ptr::NonNull};
use benches::bench;
use bevy_ecs::{
change_detection::MaybeLocation,
component::{ComponentCloneBehavior, ComponentDescriptor, StorageType},
prelude::*,
ptr::OwningPtr,
};
use criterion::{criterion_group, Criterion};
criterion_group!(benches, get, get_mut, insert_remove);
fn create_world() -> World {
let mut world = World::new();
for _ in 0..500 {
// SAFETY: Uses zero-sized value, never drops
unsafe {
let resource_id =
world.register_component_with_descriptor(ComponentDescriptor::new_with_layout(
"",
StorageType::SparseSet,
Layout::new::<()>(),
None,
true,
ComponentCloneBehavior::Default,
None,
));
world.insert_resource_by_id(
resource_id,
OwningPtr::new(NonNull::dangling()),
MaybeLocation::caller(),
);
}
}
world
}
#[derive(Resource)]
struct R;
pub fn get(criterion: &mut Criterion) {
let mut world = create_world();
world.insert_resource(R);
criterion.bench_function(bench!("get"), |bencher| {
bencher.iter(|| world.get_resource::<R>());
});
}
pub fn get_mut(criterion: &mut Criterion) {
let mut world = create_world();
world.insert_resource(R);
criterion.bench_function(bench!("get_mut"), |bencher| {
bencher.iter(|| {
black_box(world.get_resource_mut::<R>());
});
});
}
pub fn insert_remove(criterion: &mut Criterion) {
let mut world = create_world();
criterion.bench_function(bench!("insert_remove"), |bencher| {
bencher.iter(|| {
world.insert_resource(R);
black_box(&mut world);
world.remove_resource::<R>()
});
});
}
+2 -1
View File
@@ -1,4 +1,4 @@
use bevy_ecs_macro_logic::component::DeriveComponent;
use bevy_ecs_macro_logic::component::{DeriveComponent, StorageTy};
use bevy_macro_utils::fq_std::FQOption;
use proc_macro2::TokenStream;
use quote::quote;
@@ -10,6 +10,7 @@ pub fn derive_resource(ast: &mut DeriveInput) -> TokenStream {
Ok(value) => value,
Err(e) => return e.into_compile_error(),
};
derive_component.storage = StorageTy::SparseSet;
let struct_name = &ast.ident;
let (_, type_generics, _) = &ast.generics.split_for_impl();