Call visitor only in one place in bodies of folds for RangeInclusive#156654
Conversation
…sive Prior attempt: eb5b096 I rewrote the loops in `spec_try_fold` and `spec_try_rfold` to call argument function only inside a loop body instead of having extra single call after the loop. I expect this change to make the loop body a single callsite for lambdas passed into closed range iterators (`x..=y`). In such cases, it would encourage LLVM to inline the lambda and remove the instantiation of its function. Also, it would reduce code of `try_fold` calls where LLVM previously inlined the lambda body twice. I handled the danger of overflow or underflow by switching from `while` loops to loops with postcondition. I also checked manually that the LLVM do not fail to optimize loops. Example test (compiled using `rustc +stage1 .\sum.rs --crate-type=dylib -O --emit=llvm-ir`): ```rust pub fn mysum(a:u32, b: u32)->u32{ (a..=b).sum() } ``` Output was successfully folded into arithmetic progression formula without loops: ```llvm-ir define noundef i32 @mysum(i32 noundef %a, i32 noundef %b) { bb28.i.i: %_0.i.not.i.i = icmp ugt i32 %a, %b br i1 %_0.i.not.i.i, label %_<fun_exit>.exit, label %bb4.preheader.i.i bb4.preheader.i.i: ; preds = %bb28.i.i %_0.i510.i.i = icmp eq i32 %a, %b br i1 %_0.i510.i.i, label %bb16.i.i, label %bb9.preheader.i.i bb9.preheader.i.i: ; preds = %bb4.preheader.i.i %0 = xor i32 %a, -1 %1 = add i32 %b, %0 %2 = add i32 %a, 1 %3 = mul i32 %1, %2 %4 = add i32 %3, %a %5 = zext i32 %1 to i33 %reass.sub = sub i32 %b, %a %6 = add i32 %reass.sub, -2 %7 = zext i32 %6 to i33 %8 = mul i33 %5, %7 %9 = lshr i33 %8, 1 %10 = trunc nuw i33 %9 to i32 %11 = add i32 %4, %10 br label %bb16.i.i bb16.i.i: ; preds = %bb9.preheader.i.i, %bb4.preheader.i.i %i.sroa.0.0.lcssa.i.i = phi i32 [ %b, %bb9.preheader.i.i ], [ %a, %bb4.preheader.i.i ] %accum.sroa.0.0.lcssa.i.i = phi i32 [ %11, %bb9.preheader.i.i ], [ 0, %bb4.preheader.i.i ] %_4.0.i.i8.i.i = add i32 %accum.sroa.0.0.lcssa.i.i, %i.sroa.0.0.lcssa.i.i br label %_<fun_exit>.exit _<fun_exit>.exit: ; preds = %bb28.i.i, %bb16.i.i %_0.sroa.0.0.i.i = phi i32 [ %_4.0.i.i8.i.i, %bb16.i.i ], [ 0, %bb28.i.i ] ret i32 %_0.sroa.0.0.i.i } ```
|
rustbot has assigned @Mark-Simulacrum. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
Is there a way to add codegen test that checks that output has no loops? |
|
@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.
…ove_range_inclusive_fold, r=<try> Call visitor only in one place in bodies of folds for RangeInclusive
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (964a43b): 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 -1.7%, secondary -2.7%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary 2.6%, secondary 6.5%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeResults (primary -0.1%, secondary 0.1%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 511.658s -> 512.106s (0.09%) |
|
@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.
…ove_range_inclusive_fold, r=<try> Call visitor only in one place in bodies of folds for RangeInclusive
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (c41e97d): 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 1.1%, secondary 0.7%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (secondary -6.2%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeResults (primary -0.0%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 530.554s -> 514.219s (-3.08%) |
|
r? libs |
|
The review request |
|
r? libs |
|
The review request |
Yes, you should be able to add a test somewhere under lib-optimizations in codegen-llvm I think. See e.g. https://github.com/rust-lang/rust/blob/main/tests/codegen-llvm/lib-optimizations/iter-sum.rs I'm not sure how easy it'll be to guarantee no loops, but you may be able to come up with some form of CHECK-NOT for |
|
Seems like there's no meaningful perf-measured improvement from this. Do you think that the extra implementation complexity is still justified? I think I could go either way. Implementation wise this looks fine to me, thanks for adding some more test coverage! |
View all comments
Prior attempt: eb5b096
I rewrote the loops in
spec_try_foldandspec_try_rfoldto call argument function only inside a loop body instead of having extra single call after the loop.I expect this change to make the loop body a single callsite for lambdas passed into closed range iterators (
x..=y). In such cases, it would encourage LLVM to inline the lambda and remove the instantiation of its function. Also, it would reduce code oftry_foldcalls where LLVM previously inlined the lambda body twice.I handled the danger of overflow or underflow by switching from
whileloops to loops with postcondition.I also checked manually that the LLVM do not fail to optimize loops.
Example test (compiled using
rustc +stage1 .\sum.rs --crate-type=dylib -O --emit=llvm-ir):Output was successfully folded into arithmetic progression formula without loops: