[perf] Avoid checking for alternate Debug on every struct field.#159120
[perf] Avoid checking for alternate Debug on every struct field.#159120obi1kenobi wants to merge 1 commit into
Debug on every struct field.#159120Conversation
|
|
|
Requested reviewer is already assigned to this pull request. Please choose another assignee. |
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
… r=<try> [perf] Avoid checking for alternate `Debug` on every field.
This comment has been minimized.
This comment has been minimized.
| builder.finish() | ||
| } else { | ||
| let mut builder = builders::debug_struct_new($formatter, $name); | ||
| $(builder.field_nonpretty($field_name, $value);)+ |
There was a problem hiding this comment.
Could we add a regression test covering derived named structs with 1–5 fields under both {:?} and {:#?}, since the existing focused test only covers two fields?
There was a problem hiding this comment.
Yes, happy to add the test. Just wanted to make sure fmt-debug-derive did get faster as I expected.
Will add it probably tonight.
There was a problem hiding this comment.
Done! Apologies for the wait. Also updated the PR description with an observation about an analogous optimization for tuple structs.
|
Finished benchmarking commit (ebaa02e): comparison URL. Overall result: no relevant changes - no action neededBenchmarking means the PR may be perf-sensitive. Consider adding rollup=never if this change is not fit for rolling up. @rustbot label: -S-waiting-on-perf -perf-regression Instruction countThis perf run didn't have relevant results for this metric. Max RSS (memory usage)Results (primary 3.1%, secondary 2.6%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (secondary -0.8%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeResults (primary 0.0%, secondary 0.0%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 490.729s -> 490.282s (-0.09%) |
|
I'm still learning the ropes of how perf-testing is set up, and I was a bit surprised that we don't seem to have |
|
What you're linking is instruction count results and |
|
Oops, I was looking at the compile time tab 🤦 |
d92761a to
809837c
Compare
Debug on every field.Debug on every struct field.
809837c to
302e754
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
Updated with test cases and doc comments, but leaving the implementation identical to the benchmarked code. |
|
☔ The latest upstream changes (presumably #159444) made this pull request unmergeable. Please resolve the merge conflicts by rebasing. |
|
Hmm, it's a bit suspicious that removing just a single condition that performs a very simple operation, and I'd expect it would be almost perfectly branch-predicted, has such an effect. I tested it locally with cachegrind in $ cargo run --bin collector profile_runtime cachegrind `rustup +48e8ec6f05057f942a97187dde770a118e05f42c which rustc` --rustc2 `rustup +ebaa02e6d9bfb6b35b6e45eaccab8b7ebb9c6461 which rustc` fmt-debug-derive --group fmt`and it seems that there is a difference in the I wonder if this is caused more by newly added inlining, rather than removing the if condition. Just out of curiosity, I tried added forced inlining (though probably it won't work like I expect it to) in #112049, to see if that has a similar effect. |
|
Hmm, yeah, this kind of inlining didn't really have any effect. @nnethercote Could you please take a look at this, since you did some work on optimizing these debug builders previously, IIRC? It looks reasonable to me, though I'm not sure how large would the effect be in real-world programs. |
The
fmt-debug-derivebenchmark shows there's a -6.78% instruction win to be had by not checking for alternate ("pretty") formatting for every field in the pre-written 1-5 arity cases of derivedDebugstruct representations.Since non-alternate mode is the default, add a fast path that supports only checking for alternate mode once total per formatting invocation, rather than once per field per invocation.
It's likely that the analogous methods for tuple structs would benefit from a similar change, but the
fmt-debug-derivebenchmark doesn't include this case. If the reviewers are amenable to it, I'd be happy to add both a new benchmark and, if perf-positive, make the analogous tuple struct optimization.r? @Kobzol
AI disclosure: The optimization opportunity here was discovered as part of a systematic probe for missed optimizations utilizing both traditional and AI tools. The code here was initially prototyped and vetted by AI tools, followed by additional manual work. I secured approval in advance from the reviewer I pinged. I stand behind the quality of the code I'm submitting, and I vouch it's as good or better compared to if I had written every line by my own hand.