-
Notifications
You must be signed in to change notification settings - Fork 320
Change the default hasher to rapidhash #633
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
base: master
Are you sure you want to change the base?
Conversation
I'm always happy to switch to a better hash function. However I would like to give @orlp (author of foldhash) an opportunity to review this first. |
Absolutely, I'm very grateful to @orlp for the work he's done! The rapidhash v3.0 release is effectively foldhash's integer hashing approach, but replaces the It took me months to match foldhash's performance. There were many small subtleties fighting against the inliner, reducing the hashmap size, faster global seeding, the integer buffer, avoiding register clobbering, and keeping the avalanching logic in @orlp Any feedback on rapidhash itself, or how to roll it out so that people can benefit from it would be great too. I think it's strictly an upgrade on foldhash, but would happily take any feedback or critique. Also happy to chat offline. |
I took a quick look at the ┌────────────────┬────────────┬─────────────┬────────────┐
│ distr ┆ bench ┆ rapidhash-f ┆ foldhash-f │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 │
╞════════════════╪════════════╪═════════════╪════════════╡
│ u32 ┆ hashonly ┆ 0.66 ┆ 0.66 │ <-- these are just the same
│ u32pair ┆ hashonly ┆ 0.79 ┆ 0.66 │ |
│ u64 ┆ hashonly ┆ 0.79 ┆ 0.79 │ |
│ u64lobits ┆ hashonly ┆ 0.66 ┆ 0.66 │ |
│ u64hibits ┆ hashonly ┆ 0.66 ┆ 0.66 │ |
│ u64pair ┆ hashonly ┆ 0.74 ┆ 0.79 │ |
│ ipv4 ┆ hashonly ┆ 0.66 ┆ 0.66 │ |
│ ipv6 ┆ hashonly ┆ 0.75 ┆ 0.74 │ |
│ rgba ┆ hashonly ┆ 0.66 ┆ 0.66 │ |
│ accesslog ┆ hashonly ┆ 1.32 ┆ 1.31 │ |
╞════════════════╪════════════╪═════════════╪════════════╡ string benchmarks after this point
│ strenglishword ┆ hashonly ┆ 1.75 ┆ 3.54 │ <-- short string perf is impressive
│ struuid ┆ hashonly ┆ 2.92 ┆ 5.04 │ |
│ strurl ┆ hashonly ┆ 4.76 ┆ 7.07 │ |
│ strdate ┆ hashonly ┆ 1.68 ┆ 3.26 │ |
│ kilobyte ┆ hashonly ┆ 28.37 ┆ 31.12 │ <-- roughly same for medium-to-long
│ tenkilobyte ┆ hashonly ┆ 320.48 ┆ 372.53 │ <-- better again at long inputs, due to
└────────────────┴────────────┴─────────────┴────────────┘ more unrolling, questionable if desired for codesize Thus, the only meaningful differences are found in the string hashing routine. And even there, the actual implementation differences are minor (albeit the results are impressive). To quantify this, we both essentially try to feed as many bytes into the following expression as quickly as possible: state = folded_multiply(input_data0 ^ state, input_data1 ^ seed); My <= 16 byte string handling is also essentially the same as rapidhash as far as I can see, so the main differences are in how much we unroll and structure the loops to feed this expression in the medium-sized and larger loops. I think thus with a relatively small PR to foldhash I can match these results for string hashing. I would love to work together to do further experiments and improve (especially) the short-string performance of foldhash if you're interested, but I can't in good faith recommend to just replace my crate entirely, especially when the While my above post is undoubtedly not what you'd want to hear, I'd like to reiterate that the improvement for short strings is impressive, and you did good work. |
I agree this is what it's become, and appreciate that you understand it wasn't the intention! Rapidhash is derived from wyhash, which as you've said revolves around the same folded multiply foldhash is based on. The rusty parts of rapidhash have morphed into foldhash, and the byte hashing part is a derivation of the C++ rapidhash, obsessively tweaked to play nice with inlining and small string sizes. Although putting it all together has taken months of work and tweaking to get it right.
I was surprised at how similar foldhash and rapidhash are for small strings given they were both released around the same time. I couldn't tell if yourself and Nicolas had come up with it independently, or had already taken inspiration from each other?
Long string hashing is marked as cold and inline never, so hopefully the increase is minimal on the final binary. I haven't compared it, but the 17..288 input length range is what I was more concerned about increasing too much.
Some other deviations from foldhash that might contribute to the performance difference:
The rapidhash crate also offers portable hashing with full compatibility to the various C++ versions, equivalent streaming versions consuming
Absolutely, and I'd be keen to learn from you too. I'll take a look at submitting a foldhash PR this week to match the small string hashing performance. If that works we can consider if it's worth improving the long string hashing, as it would need more secrets to use the rapidhash implementation. I was also considering experimenting with some tweaks to rustc-hash afterwards, but I think your optimisations there are already close to optimal for the small strings workload! |
The Similarly, doing overlapping reads of So with the 1-3 bytes trick and the above with |
A quick status update: with some changes to inlining the largest gaps between rapidhash and foldhash are already closed. I'm still tweaking the string hash a bit further but expect a release probably somewhere next weekend. |
I've released |
I've added a PR #641 to bump foldhash to v0.2.0 since the performance is so similar. The thumbv6m test fails here because As for MSRV, rapidhash reduced its MSRV to 1.71.0 with v4.0.0, while hashbrown is 1.65.0. |
Hello,
I've recently released rapidhash v3.0.0. It uses the same integer hashing as foldhash, but comprehensively beats the bytes/string hashing performance on all platforms we've benchmarked (common server chips and M1 Max). It offers the same minimal DoS resistance and portability too.
Aside from marginally increased code size and a higher MSRV (rapidhash=1.77, foldhash=1.60) I believe there are no other downsides to changing the default to rapidhash.
If the MSRV is an issue, I can take a look at reducing it? It's only this high because the hashing algorithm is fully
const
, and efficiently reading/splitting slices in const is difficult with the older versions.Full benchmarks are available in the README and docs folder. Very happy to answer any questions or hear feedback on rapidhash. Thank you!