Repository navigation
Optimize SipHash by reordering compress instructions - #127226
Conversation
|
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @lcnr (or someone else) some time within the next two weeks. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
|
|
the primary instructions metrics won't change, but maybe we can notice a difference in cycles or wall time. not a requirement for merging though. |
This comment has been minimized.
This comment has been minimized.
Optimize SipHash by reordering compress instructions This PR optimizes hashing by changing the order of instructions in the sip.rs `compress` macro so the CPU can parallelize it better. The new order is taken directly from Fig 2.1 in [the SipHash paper](https://eprint.iacr.org/2012/351.pdf) (but with the xors moved which makes it a little faster). I attempted to optimize it some more after this, but I think this might be the optimal instruction order. Note that this shouldn't change the behavior of hashing at all, only statements that don't depend on each other were reordered. It appears like the current order hasn't changed since its [original implementation from 2012](rust-lang@fada46c#diff-b751133c229259d7099bbbc7835324e5504b91ab1aded9464f0c48cd22e5e420R35) which doesn't look like it was written with data dependencies in mind. Running `./x bench library/core --stage 0 --test-args hash` before and after this change shows the following results: Before: ``` benchmarks: hash::sip::bench_bytes_4 7.20/iter +/- 0.70 hash::sip::bench_bytes_7 9.01/iter +/- 0.35 hash::sip::bench_bytes_8 8.12/iter +/- 0.10 hash::sip::bench_bytes_a_16 10.07/iter +/- 0.44 hash::sip::bench_bytes_b_32 13.46/iter +/- 0.71 hash::sip::bench_bytes_c_128 37.75/iter +/- 0.48 hash::sip::bench_long_str 121.18/iter +/- 3.01 hash::sip::bench_str_of_8_bytes 11.20/iter +/- 0.25 hash::sip::bench_str_over_8_bytes 11.20/iter +/- 0.26 hash::sip::bench_str_under_8_bytes 9.89/iter +/- 0.59 hash::sip::bench_u32 9.57/iter +/- 0.44 hash::sip::bench_u32_keyed 6.97/iter +/- 0.10 hash::sip::bench_u64 8.63/iter +/- 0.07 ``` After: ``` benchmarks: hash::sip::bench_bytes_4 6.64/iter +/- 0.14 hash::sip::bench_bytes_7 8.19/iter +/- 0.07 hash::sip::bench_bytes_8 8.59/iter +/- 0.68 hash::sip::bench_bytes_a_16 9.73/iter +/- 0.49 hash::sip::bench_bytes_b_32 12.70/iter +/- 0.06 hash::sip::bench_bytes_c_128 32.38/iter +/- 0.20 hash::sip::bench_long_str 102.99/iter +/- 0.82 hash::sip::bench_str_of_8_bytes 10.71/iter +/- 0.21 hash::sip::bench_str_over_8_bytes 11.73/iter +/- 0.17 hash::sip::bench_str_under_8_bytes 10.33/iter +/- 0.41 hash::sip::bench_u32 10.41/iter +/- 0.29 hash::sip::bench_u32_keyed 9.50/iter +/- 0.30 hash::sip::bench_u64 8.44/iter +/- 1.09 ``` I ran this on my computer so there's some noise, but you can tell at least `bench_long_str` is significantly faster (~18%). Also, I noticed the same compress function from the library is used in the compiler as well, so I took the liberty of copy-pasting this change to there as well. Thanks `@semisol` for porting SipHash for another project which led me to notice this issue in Rust, and for helping investigate. <3
|
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (3e6e884): comparison URL. Overall result: no relevant changes - no action neededBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. @bors rollup=never Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)This benchmark run did not return any relevant results for this metric. CyclesThis benchmark run did not return any relevant results for this metric. Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 695.108s -> 696.414s (0.19%) |
|
r? @nnethercote |
|
@bors r+ |
|
🌲 The tree is currently closed for pull requests below priority 100. This pull request will be tested once the tree is reopened. |
|
@mat-1 Could you also send those improvements to https://github.com/rust-lang/rustc-stable-hash, which is in the progress of becoming the new place for the stable hashing stuff (including the siphasher). |
commented
Jul 2, 2024
|
Have you compared the generated assembly? I'm surprised that the backend doesn't already perform reordering as long as it doesn't violate data dependencies.
You'll have to be very careful with those, especially on laptop CPUs. They measure walltime, not cpu-instructions. So turbo boosts, core placement, thermal limitations etc. can all affect the results. https://std-dev-guide.rust-lang.org/development/perf-benchmarking.html |
commented
Jul 2, 2024
The amount of executed instructions should be unaffected by this change. Only the amount of cycles it takes should improve if this change does what it should do by increasing IPC. |
Yep, I put it into Godbolt earlier. And here's the benchmarks run 3 more times for good measure (I ran them alternating between master and my branch): Before
After
Interestingly, setting Before hash::sip::bench_bytes_4 7.44/iter +/- 0.11
hash::sip::bench_bytes_7 8.28/iter +/- 0.06
hash::sip::bench_bytes_8 8.34/iter +/- 0.06
hash::sip::bench_bytes_a_16 10.09/iter +/- 0.02
hash::sip::bench_bytes_b_32 13.77/iter +/- 0.10
hash::sip::bench_bytes_c_128 35.93/iter +/- 0.15
hash::sip::bench_long_str 110.21/iter +/- 0.80
hash::sip::bench_str_of_8_bytes 11.38/iter +/- 0.09
hash::sip::bench_str_over_8_bytes 12.22/iter +/- 0.09
hash::sip::bench_str_under_8_bytes 9.78/iter +/- 0.23
hash::sip::bench_u32 9.74/iter +/- 0.49
hash::sip::bench_u32_keyed 8.54/iter +/- 0.06
hash::sip::bench_u64 9.84/iter +/- 0.08
After hash::sip::bench_bytes_4 7.36/iter +/- 0.24
hash::sip::bench_bytes_7 8.24/iter +/- 0.09
hash::sip::bench_bytes_8 8.42/iter +/- 0.92
hash::sip::bench_bytes_a_16 10.14/iter +/- 0.08
hash::sip::bench_bytes_b_32 13.86/iter +/- 8.14
hash::sip::bench_bytes_c_128 35.89/iter +/- 0.09
hash::sip::bench_long_str 109.86/iter +/- 0.56
hash::sip::bench_str_of_8_bytes 11.66/iter +/- 0.27
hash::sip::bench_str_over_8_bytes 11.71/iter +/- 0.12
hash::sip::bench_str_under_8_bytes 9.72/iter +/- 0.18
hash::sip::bench_u32 10.14/iter +/- 0.25
hash::sip::bench_u32_keyed 8.59/iter +/- 0.04
hash::sip::bench_u64 9.85/iter +/- 0.04
|
commented
Jul 4, 2024
commented
Jul 4, 2024
|
☀️ Test successful - checks-actions |
commented
Jul 4, 2024
|
Finished benchmarking commit (f6fa358): comparison URL. Overall result: ❌ regressions - no action needed@rustbot label: -perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)Results (primary -2.2%, secondary -2.0%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (secondary 7.9%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 720.847s -> 740.571s (2.74%) |
This PR optimizes hashing by changing the order of instructions in the sip.rs
compressmacro so the CPU can parallelize it better. The new order is taken directly from Fig 2.1 in the SipHash paper (but with the xors moved which makes it a little faster). I attempted to optimize it some more after this, but I think this might be the optimal instruction order. Note that this shouldn't change the behavior of hashing at all, only statements that don't depend on each other were reordered.It appears like the current order hasn't changed since its original implementation from 2012 which doesn't look like it was written with data dependencies in mind.
Running
./x bench library/core --stage 0 --test-args hashbefore and after this change shows the following results:Before:
After:
I ran this on my computer so there's some noise, but you can tell at least
bench_long_stris significantly faster (~18%).Also, I noticed the same compress function from the library is used in the compiler as well, so I took the liberty of copy-pasting this change to there as well.
Thanks @Semisol for porting SipHash for another project which led me to notice this issue in Rust, and for helping investigate. <3