-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Optimize param validation through get_param(...) -> Option<Out>
#15606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The steps taken to get here look as following:
To sum it up, without going the additional step into proposition 1.2 we simply cannot reduce double checks in executors. An alternative to 1.2 idea could split "fetching" into 2 steps:
|
Marking it as ready for review since code is basically done, just needs benchmarks |
# Objective Benchmark overhead of validation for: - `DynSystemParam`, - `ParamSet`, - combinator systems. Needed for #15606 ## Solution As noted in objective, I've added 3 benchmarks, where each uses an excessive amount of the specific functionality. I benchmark on the level of schedules, rather than individual `validate_param` calls, so we get a better idea how changes to the code impact memory-lookup, etc. related side effects. ## Testing ``` param/combinator_system/8_piped_systems time: [1.7560 µs 1.7865 µs 1.8180 µs] change: [+4.5244% +6.7955% +9.1413%] (p = 0.00 < 0.05) Performance has regressed. Found 2 outliers among 100 measurements (2.00%) 1 (1.00%) high mild 1 (1.00%) high severe param/combinator_system/8_dyn_params_system time: [89.354 ns 89.790 ns 90.300 ns] change: [+0.6751% +1.6825% +2.6842%] (p = 0.00 < 0.05) Change within noise threshold. Found 9 outliers among 100 measurements (9.00%) 6 (6.00%) high mild 3 (3.00%) high severe param/combinator_system/8_variant_param_set_system time: [88.295 ns 89.202 ns 90.208 ns] change: [+0.1320% +1.0060% +1.8482%] (p = 0.02 < 0.05) Change within noise threshold. Found 4 outliers among 100 measurements (4.00%) 4 (4.00%) high mild ``` 2 back-to-back runs of the benchmarks, there is quire a lot of noise, can use feedback on fixing that
Before (
After:
There is a lot of noise in the measurements, but at the very least I don't think this is a regression It'd be good if few more people measured, I use |
@MiniaczQ, this looks like it should be reviewable right? There's a lot of merge conflicts now though 😅 |
It used to be, all the ideas are here, it just affects such basic APIs that everything conflicts. Now this also needs to mesh with fallible systems, so that's more work |
The changes look reasonable, but it seems like a rebase would change enough that I should wait before clicking Approve. I don't quite follow the motivation. It seems like the original goal was to avoid a double lookup, but then you determined that wouldn't be possible. Is the current goal to remove duplication between The changes to Do we expect anyone use fallible parameters with |
The core issue is that we cannot validate params anywhere, but directly before running a system. In case of
It always could have failed, but we agreed a failure results in a panic. There aren't many unwraps in the core code, since failures are forwarded to
The exact API wasn't agreed upon, i guess this is a good split point for the rewrite, since it drastically reduces the API changes. If we want non-panicking default we can do a followup. |
Yup, that makes sense! I think I'm just asking to update the "Objective" section in the PR. It links to an issue that talks about getting rid of all of the double lookups, but this PR doesn't get rid of them for ordinary systems in the multi-threaded executor, so it's not immediately clear what the goal is here.
It depends on the parameter, though, right? Like, The difference I see between this and things like In the future, we might even be able to add a I'm not suggesting that any of that should be in this PR, of course! But I think it might be worth making this PR smaller by moving the |
#param::get_param(&mut self.param_states.#index, &self.system_meta, self.world, self.change_tick) | ||
} | ||
}; | ||
// `ParamSet::get_param` ensures all sub-params are accessible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this true? It looks like get_param
below does an unconditional Some(param)
. Did you mean to call validate_param
there?
It would be nice to keep some way of delaying validation on the sub-parameters. Like, the safe PipeSystem
example will want to to delay validation of the second parameter, for the same reason you're doing so for the real PipeSystem
. Maybe that could be spelled ParamSet<(A::Param, Option<B::Param>)>
? Although doing a blanket impl on Option
would change the semantics of Option<Single>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't tell now, this was made quite a while ago.
We'll definitely want someone experience check the ParamSet, DynamicSystemParam, etc.
We try to store param's ability to fail in type and design an API around that. Since this PR I've read some discussion about fallibility and few new decisions include removing per-system param fail behavior. Also, since this is a pretty old PR with lots of outdated information and crap ton of merge conflicts, I'd rather have it closed whenever someone picks up the overall issue. |
Objective
Fixes #15505 through a new solution which came up during development.
It changes some of the original assumptions about checking all system params before running the systems,
which isn't correct for combinator systems (they can invalidate their own sub-system params).
Alternative to #15571
Solution
get_param
returns anOption
, which makesrun_unsafe
also return an option.validate_param
now defaults toget_param().is_some()
and we overwrite it for non-send and grouped params to behave correctly.The original assumption about validating all params before running a system is no longer valid.
Combinator system can invalidate it's own params in a case of:
a.pipe(b).pipe(a)
whereb
removes required params fora
.Because of that, we will no longer validate all params of a system, but only those of the first one.
This allows us to prevent spawning tasks if we cannot start running a system and reduced access overhead, because we only check a single subsystem's params immediately followed by getting them (albeit in another thread).
Testing
Ran parts of CI locally,
Ran
fallible_params
example.