Explore other potential approaches to bevy system piping. #8857
Labels
A-ECS
Entities, components, systems, and events
C-Enhancement
A new feature
X-Controversial
There is active debate or serious implications around merging this PR
What problem does this solve or what need does it fill?
As bevy system piping currently is, it is essentially useless to users.
As I understand it, currently piping systems is essentially composing two functions into one system, which has all query parameters as part of it, and has no performance benefits. In most cases, instead of using bevy system piping it is both more ergonomic and nearly the same amount of boiler plate to simply write an enclosing system for two or more functions than to use bevy piping.
To a user, this is what a intuitive method of achieving piping without making use of bevy piping might look like:
Although this is intuitive, and requires less generic magic than bevy system piping, while allowing finer control between how functions are composed, it has two clear major downside:
Performance: Current bevy piping suffers from this same issue, that all queries required by any function piped into the system is seen as a dependency for the entire duration of the pipeline. My proposed design would likely allow bevy to optimize this such that dependencies are only needed during the specific function that queries them.
Boiler Plate: Although this is nearly idiomatic rust code, bevy piping neatly cleans up this pattern such that the user doesn't find themselves writing boilerplate pipeline glue functions like
tint_bars_above_foo
20 times for each program.System piping is a significant step up from this naive approach, as it addresses the second issue of boiler plate in a far cleaner way. However, I believe that until system piping also brings with it performance gains over the naive solution, it will be wasting a significant amount of potential.
The first problem with this naive code however, makes the potential performance gains of bevy piping jump out to me, as it shows that having dependencies be analyzed per-pipeline instead of for each individual system can easily lead to missed parallelism. It seems that this is a well known and documented drawback of system piping, however it cannot be avoided ergonomically at the user level, even with a bespoke piping system.
What solution would you like?
A more performance minded individual might attempt to do something like this, in order to have bevy analyze each component of the pipeline as having separate dependencies, thus increasing parallelism.
This is a big step up from both bevy piping, and the naive solution in terms of performance, however it still has a boilerplate disadvantage, and is less idiomatic rust, as it essentially recreates the in/out variable pattern common in C++ and C#, which is mainly discouraged in rust. However, the parallelization gains are likely worth this non-idiomatic approach in performance sensitive systems.
However, I think that bevy piping could be updated to ergonomically abstract over this approach, allowing the user to write cleaner and more idiomatic rust code, while still reaping the exact same, or potentially better performance benefits.
I suggest that we have the In types for function arguments abstract over Res, with returns abstracting over ResMut like in the example, while keeping an api similar to current bevy piping. This would allow users to write both performant and idiomatic code and might look something like the code below.
Note that for increased ergonomics I use pipe() like the chain operator, where (sys_1,sys_2,sys_3).pipe() pipes sys_1 into sys_2, sys_2 into sys_3.
I believe that this system is possible to generate with bevy as it is now, and would be to great benefit to end users. I am however, unfamiliar with bevy internals and understand if for some reason this is infeasible or currently not worth the effort, but still believe it would be very useful to the bevy project to reconsider system piping such that it allows separate dependency analysis for each component of the pipeline in some way, even though it likely won't end up looking like anything I have suggested.
What alternative(s) have you considered?
You could keep piping the way it is, with extremely suboptimal performance and encourage end users to vendor their own piping solution for each project, wasting time for everybody, and leading to non-idiomatic code.
Additional context
I'm not familiar with bevy internals, so their may be some obvious things I'm missing out on
The text was updated successfully, but these errors were encountered: