Closed rj00a closed 1 month ago
Replace bevy ECS with evenio
no more bevy :(?
Replace bevy ECS with evenio
no more bevy :(?
kill bevy with FIRE! ๐ฅ
Replace bevy ECS with evenio.
May I ask why? bevy_ecs outperforms it on its own benchmark everywhere except on random access, where bevy is twice the time (because it's a dual lookup for an entity->archetype->data), but of times so tiny they are just a pointer indirection cost anyway?
Here's my benchmark times using evenio's own benchmark with the latest version of both evenio and bevy_ecs (so git for evenio and 0.14.2 for bevy, no code changes needed for their benchmark code, it all just worked) for their benchmarks that have bevy in it:
add_components
(honestly I expect bevy to be slightly slower here as archetypes styles often are due to more copies, so... weird?):
Timer precision: 70 ns add_components fastest โ slowest โ median โ mean โ samples โ iters โโ spawn_many_comps_40_bevy 1.662 ยตs โ 44.46 ยตs โ 1.705 ยตs โ 2.952 ยตs โ 100 โ 1000 โฐโ spawn_many_comps_n_evenio โ โ โ โ โ โโ 1 276.1 ns โ 1.73 ยตs โ 282.2 ns โ 305.4 ns โ 100 โ 1000 โโ 2 463.1 ns โ 2.065 ยตs โ 471.1 ns โ 506.9 ns โ 100 โ 1000 โโ 3 688.1 ns โ 2.177 ยตs โ 699.1 ns โ 735.2 ns โ 100 โ 1000 โโ 5 1.189 ยตs โ 3.053 ยตs โ 1.212 ยตs โ 1.253 ยตs โ 100 โ 1000 โโ 10 2.637 ยตs โ 6.394 ยตs โ 2.664 ยตs โ 2.762 ยตs โ 100 โ 1000 โโ 15 2.67 ยตs โ 11.34 ยตs โ 4.419 ยตs โ 4.437 ยตs โ 100 โ 1000 โโ 20 2.762 ยตs โ 11.69 ยตs โ 3.285 ยตs โ 3.384 ยตs โ 100 โ 1000 โโ 25 3.339 ยตs โ 11.59 ยตs โ 3.916 ยตs โ 4.176 ยตs โ 100 โ 1000 โโ 30 5.099 ยตs โ 16.01 ยตs โ 5.131 ยตs โ 5.406 ยตs โ 100 โ 1000 โฐโ 40 6.76 ยตs โ 20.27 ยตs โ 7.936 ยตs โ 8.12 ยตs โ 100 โ 1000
iter
(I expected bevy to be faster here, and it indeed was in much of it, but there seems to be a weird bit of noise... somewhere, it's not scaling for either of them as it should on the mandelbrot stuff... Also I find it odd they only look up a single component...):
Timer precision: 70 ns iter fastest โ slowest โ median โ mean โ samples โ iters โโ iter_mandelbrot_bevy โ โ โ โ โ โ โฐโ true โ โ โ โ โ โ โโ 1 38.87 ยตs โ 106.5 ยตs โ 64.03 ยตs โ 66.08 ยตs โ 100 โ 100 โ โโ 2 48.01 ยตs โ 130.8 ยตs โ 71.97 ยตs โ 72.85 ยตs โ 100 โ 100 โ โโ 3 63.36 ยตs โ 133.7 ยตs โ 97.65 ยตs โ 98.05 ยตs โ 100 โ 100 โ โโ 8 127 ยตs โ 213.1 ยตs โ 154.8 ยตs โ 156.5 ยตs โ 100 โ 100 โ โโ 16 199.2 ยตs โ 598.1 ยตs โ 239.2 ยตs โ 277.9 ยตs โ 100 โ 100 โ โโ 32 220.5 ยตs โ 828.7 ยตs โ 337.8 ยตs โ 349.6 ยตs โ 100 โ 100 โ โโ 64 431.3 ยตs โ 673.7 ยตs โ 540 ยตs โ 538.6 ยตs โ 100 โ 100 โ โโ 128 454.5 ยตs โ 1.673 ms โ 759.5 ยตs โ 773.4 ยตs โ 100 โ 100 โ โโ 256 834.4 ยตs โ 2.127 ms โ 1.437 ms โ 1.459 ms โ 100 โ 100 โ โโ 512 2.077 ms โ 3.302 ms โ 2.748 ms โ 2.764 ms โ 100 โ 100 โ โฐโ 1024 2.737 ms โ 9.691 ms โ 5.098 ms โ 4.412 ms โ 100 โ 100 โโ iter_mandelbrot_evenio โ โ โ โ โ โ โฐโ true โ โ โ โ โ โ โโ 1 56.97 ยตs โ 1.506 ms โ 111.4 ยตs โ 156.6 ยตs โ 100 โ 100 โ โโ 2 63.31 ยตs โ 775.3 ยตs โ 186.8 ยตs โ 189.3 ยตs โ 100 โ 100 โ โโ 3 62.99 ยตs โ 503.5 ยตs โ 78.32 ยตs โ 128.9 ยตs โ 100 โ 100 โ โโ 8 74.49 ยตs โ 375.4 ยตs โ 141.4 ยตs โ 159.8 ยตs โ 100 โ 100 โ โโ 16 90.65 ยตs โ 978.5 ยตs โ 229.7 ยตs โ 242.3 ยตs โ 100 โ 100 โ โโ 32 110.7 ยตs โ 919.3 ยตs โ 243.2 ยตs โ 247 ยตs โ 100 โ 100 โ โโ 64 145 ยตs โ 1.086 ms โ 337.8 ยตs โ 343.9 ยตs โ 100 โ 100 โ โโ 128 227.7 ยตs โ 800.8 ยตs โ 449.3 ยตs โ 465.5 ยตs โ 100 โ 100 โ โโ 256 411.8 ยตs โ 2.352 ms โ 636.3 ยตs โ 662.1 ยตs โ 100 โ 100 โ โโ 512 687.1 ยตs โ 1.418 ms โ 709 ยตs โ 765.7 ยตs โ 100 โ 100 โ โฐโ 1024 1.291 ms โ 2.342 ms โ 1.357 ms โ 1.464 ms โ 100 โ 100 โโ iter_simple_bevy โ โ โ โ โ โ โโ false โ โ โ โ โ โ โ โโ 1 7.806 ns โ 44.63 ns โ 11.79 ns โ 11.98 ns โ 100 โ 25600 โ โ โโ 10 18.97 ns โ 51.59 ns โ 18.99 ns โ 19.31 ns โ 100 โ 51200 โ โ โโ 100 64.47 ns โ 135.7 ns โ 64.48 ns โ 65.53 ns โ 100 โ 12800 โ โ โโ 1000 630.3 ns โ 2.205 ยตs โ 630.5 ns โ 685.9 ns โ 100 โ 800 โ โ โโ 10000 6.298 ยตs โ 25.85 ยตs โ 12.55 ยตs โ 10.02 ยตs โ 100 โ 100 โ โ โโ 100000 53.55 ยตs โ 150.1 ยตs โ 125.1 ยตs โ 99 ยตs โ 100 โ 100 โ โ โฐโ 1000000 594 ยตs โ 1.416 ms โ 1.115 ms โ 1.034 ms โ 100 โ 100 โ โฐโ true โ โ โ โ โ โ โโ 1 1.789 ยตs โ 51.56 ยตs โ 1.958 ยตs โ 5.421 ยตs โ 100 โ 100 โ โโ 10 4.938 ยตs โ 41.98 ยตs โ 9.783 ยตs โ 11.13 ยตs โ 100 โ 100 โ โโ 100 13.95 ยตs โ 74.89 ยตs โ 18.28 ยตs โ 21.41 ยตs โ 100 โ 100 โ โโ 1000 20.77 ยตs โ 90.14 ยตs โ 30.4 ยตs โ 33.6 ยตs โ 100 โ 100 โ โโ 10000 25.73 ยตs โ 186 ยตs โ 42.01 ยตs โ 44.17 ยตs โ 100 โ 100 โ โโ 100000 67.79 ยตs โ 240.5 ยตs โ 111.2 ยตs โ 121.2 ยตs โ 100 โ 100 โ โฐโ 1000000 217.9 ยตs โ 937.3 ยตs โ 476 ยตs โ 477.7 ยตs โ 100 โ 100 โฐโ iter_simple_evenio โ โ โ โ โ โโ false โ โ โ โ โ โ โโ 1 30.02 ns โ 658.6 ns โ 30.38 ns โ 131.9 ns โ 100 โ 25600 โ โโ 10 37.88 ns โ 86.93 ns โ 40.53 ns โ 41.42 ns โ 100 โ 25600 โ โโ 100 151 ns โ 271 ns โ 151.4 ns โ 153.3 ns โ 100 โ 3200 โ โโ 1000 1.284 ยตs โ 4.563 ยตs โ 1.286 ยตs โ 1.465 ยตs โ 100 โ 400 โ โโ 10000 12.57 ยตs โ 33.79 ยตs โ 25.07 ยตs โ 19.54 ยตs โ 100 โ 100 โ โโ 100000 106.8 ยตs โ 308.9 ยตs โ 220.3 ยตs โ 194.3 ยตs โ 100 โ 100 โ โฐโ 1000000 1.341 ms โ 4.775 ms โ 2.521 ms โ 2.478 ms โ 100 โ 100 โฐโ true โ โ โ โ โ โโ 1 59.16 ns โ 115.6 ns โ 59.24 ns โ 64.57 ns โ 100 โ 12800 โโ 10 12.66 ยตs โ 260.4 ยตs โ 34.37 ยตs โ 48.77 ยตs โ 100 โ 100 โโ 100 20.55 ยตs โ 252.3 ยตs โ 31.27 ยตs โ 39.79 ยตs โ 100 โ 100 โโ 1000 42.22 ยตs โ 201.3 ยตs โ 89.62 ยตs โ 94.8 ยตs โ 100 โ 100 โโ 10000 58.51 ยตs โ 438.7 ยตs โ 118.2 ยตs โ 152.4 ยตs โ 100 โ 100 โโ 100000 79.11 ยตs โ 710.3 ยตs โ 232.8 ยตs โ 257.6 ยตs โ 100 โ 100 โฐโ 1000000 289 ยตs โ 2.196 ms โ 765 ยตs โ 866.6 ยตs โ 100 โ 100
random_access
(I expect bevy to be twice the cost here since 2 pointer indirections instead of one for the secondary index/cache, but again, these are so fast that the times are just noise, the test has to have a million entities to even get to the times it's at, which is literally just memory pressure timings at this point as they run literally no code beyond a black_box access in the tests, doing any work at all will flatten this):
Timer precision: 29 ns random_access fastest โ slowest โ median โ mean โ samples โ iters โโ random_access_bevy 1.842 ms โ 3.721 ms โ 2.02 ms โ 2.18 ms โ 100 โ 100 โฐโ random_access_evenio 917.1 ยตs โ 1.279 ms โ 1.052 ms โ 1.056 ms โ 100 โ 100
I can't find the discussion right now, but this has been discussed before. We are looking to replace bevy primarily because of development ergonomics. A lot of the code we write relies very heavily on events that trigger logic.
I personally don't think any developers are motivated to continue this rewrite. At the moment @rj00a has mysteriously disappeared and I seem to be the main person still working on it. My development effort is currently focused on 1.21 and after that I would rather add features then undertake a rewrite. If someone with a lot of spare time and motivation shows up this might happen but it seems unlikely.
The evenio docs eludes to some of the reasoning https://docs.rs/evenio/latest/evenio/tutorial/#why-evenio
A lot of the code we write relies very heavily on events that trigger logic.
The evenio docs eludes to some of the reasoning https://docs.rs/evenio/latest/evenio/tutorial/#why-evenio
bevy_ecs
also handles events, though it does it in a more controllable way with less overhead as they are batched and run at-once in a system with parallel support. What it looks like evenio does from my little of experimenting with it is it calls event handlers immediately, which is equivalent to bevy_ecs
's ability to run a system on demand (many times in a frame if you so wish), except bevy's has less virtual dispatch overhead as the archetypes can be immediately known, statically if so wished (or even dynamically dispatched if you wanted to go that way, ala' how evenio does it).
I experimented with evenio but I didn't see any feature it had that bevy_ecs lacked, and bevy_ecs has a great many features that it lacks, and it had no performance boost to offset it either (and indeed you lose out in a great amount of performance in many areas with multi-threading as it can only multi-thread 'within' a system (if the user chooses to), not 'across' systems, and you cannot often multi-thread within a system and even most of the time when you can it's not worth it due to the task dispatch overhead and the low amount of iterations within most systems).
But yeah, in short if you want to run something to handle an 'event' now then bevy has run-on-demand systems for that, but honestly you shouldn't except in specialized cases (which do exist), that's needless performance overhead for no reason otherwise and makes it harder to follow 'when' something happens.
I'm going to close and unpin this issue since the main driver behind it (@rj00a) is MIA for now. We can always reopen it later.
IMO, the changes suggested in this issue (particularly the switch to evenio
) are a non-starter because it requires too much effort. We are better off continuing to iterate on the current version.
Here are the steps that need to be taken to rewrite the project and get it updated to the latest version of the game, since folks were asking.
Should be done roughly in this order, one or more PRs per check.
criterion
todivan
for benchmarking.evenio
and depend on that.Arc
s for easy sharing and fast lookups. Registry modification is then possible if the refcount is 1.compio
.valence_anvil
,valence_command
.valence_protocol
. Might need a rewrite.