bevy icon indicating copy to clipboard operation
bevy copied to clipboard

Added benchmark for large schedule

Open Trashtalk217 opened this issue 1 year ago • 4 comments

Objective

We currently have no benchmarks for large worlds with many entities, components and systems. Having a benchmark for a world with many components is especially useful for the performance improvements needed for relations. This is also a response to this comment from cart.

I'd like both a small bevy_ecs-scoped executor benchmark that generates thousands of components used by hundreds of systems.

Solution

I use dynamic components and components to construct a benchmark with 2000 components, 4000 systems, and 10000 entities.

Some notes

  • ~I use a lot of random entities, which creates unpredictable performance, I should use a seeded PRNG.~
  • Not entirely sure if everything is ran concurrently currently. And there are many conflicts, meaning there's probably a lot of first-come-first-serve going on. Not entirely sure if these benchmarks are very reproducible.
  • Maybe add some more safety comments
  • Also component_reads_and_writes() is about to be deprecated #16339, but there's no other way to currently do what I'm trying to do.

Trashtalk217 avatar Dec 01 '24 23:12 Trashtalk217

One option is to remember access_components yourself. You could give base_system a Local<Vec<ComponentId>> param and build it with LocalBuilder(access_components.collect()), or you could give it an ordinary &[ComponentId] param and do let access_components: Vec<_> = access_components.collect(); then .build_system(move |query: Query<FilteredEntityMut>| base_system(&access_components, query)).

That might also help reduce noise when testing changes to the implementation of Access, since the performance of component_reads_and_writes() will change, but a real system wouldn't be calling it.

chescock avatar Dec 02 '24 01:12 chescock

One option is to remember access_components yourself. You could give base_system a Local<Vec<ComponentId>> param and build it with LocalBuilder(access_components.collect()), or you could give it an ordinary &[ComponentId] param and do let access_components: Vec<_> = access_components.collect(); then .build_system(move |query: Query<FilteredEntityMut>| base_system(&access_components, query)).

That might also help reduce noise when testing changes to the implementation of Access, since the performance of component_reads_and_writes() will change, but a real system wouldn't be calling it.

I'm a fan of passing it as an argument, I'll do that. I'm hesitant to use Locals in this case because that's effectively adding another little bit of complexity (I believe Locals are Resources underneath, not entirely sure) which could potentially add to the variance of the benchmark.

Trashtalk217 avatar Dec 02 '24 23:12 Trashtalk217

I'm a fan of passing it as an argument, I'll do that. I'm hesitant to use Locals in this case because that's effectively adding another little bit of complexity (I believe Locals are Resources underneath, not entirely sure) which could potentially add to the variance of the benchmark.

I agree that using a closure is simpler! But in case you're curious: Locals store their value right in the param state, not in a Resource or anything. So accessing them should be just a pointer offset from the system struct, same as a closure capture.

chescock avatar Dec 03 '24 02:12 chescock

It might be worth trying this as a stress test example since it's so noisy. That way we could bench using tracy and run it for longer to reduce the noise.

hymm avatar Dec 03 '24 22:12 hymm

Okay, I made some changes and I'm a lot happier with it. Two questions remain:

  • I don't like pulling in all DefaultPlugins to be able to log fps, I want to do this with MinimalPlugins, but I have not gotten that to work yet
  • Next I'm wondering about the defaults, currently, we have e = 10000, c = 2000, and s = 500, but I'm not sure if that's very representative. It's configurable right now, but I'd like to have the default make some sense. I'm also wondering if the current base_system might be too heavy to be representative.

I'll also do a separate bench for just comparing access comparisons, but that'll be a separate PR.

Trashtalk217 avatar Dec 07 '24 13:12 Trashtalk217