Skip to content

Questionable hash seed reuse between RepartitionExec and HashJoinExec #15620

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

Open
ctsk opened this issue Apr 7, 2025 · 3 comments · May be fixed by #15783
Open

Questionable hash seed reuse between RepartitionExec and HashJoinExec #15620

ctsk opened this issue Apr 7, 2025 · 3 comments · May be fixed by #15783
Labels
bug Something isn't working

Comments

@ctsk
Copy link
Contributor

ctsk commented Apr 7, 2025

Describe the bug

HashJoinExec and RepartitionExec both instantiate a ahash::RandomState with the same seed:

Partitioning::Hash(exprs, num_partitions) => BatchPartitionerState::Hash {
exprs,
num_partitions,
// Use fixed random hash
random_state: ahash::RandomState::with_seeds(0, 0, 0, 0),
hash_buffer: vec![],
},

let random_state = RandomState::with_seeds(0, 0, 0, 0);

This means that both operators compute the same hash functions. This is problematic when a HashJoinExec has a RepartitionExec as a child: Because the RepartitionExec partitions based on the lowest k bits of the hash, each HashJoinStream finds that all the hashes it computes have the same k lowest bits! In theory this could make the HashTable work less efficiently / have more collisions than expected.

Why in theory? Because despite my best attempts, I could not construct a benchmark that showed that changing the hash seed for HJ made a performance difference. I suspect this is due to a combination of the fact that the underlying hashtable uses open addressing, hashbrown storing a bitmask based on the highest bits in the buckets to do some early filtering and other bottlenecks in the HJ.

Is there anything I missed?

To Reproduce

Use this branch: https://github.com/ctsk/datafusion/tree/experiment/collision-reproducer

I patched the HashJoinExec so that it emits a warning when all row hashes of a batch share common least significant bits.

Run RUST_LOG=warn ./bench.sh run tpch and see lots of warning emitted.

Expected behavior

No response

Additional context

Fix is simple: Change the hash seed in HashJoinExec - or don't provide any seed (use Default::default() like we do in AggregationExec.

@ctsk ctsk added the bug Something isn't working label Apr 7, 2025
@alamb
Copy link
Contributor

alamb commented Apr 9, 2025

Having more repeatable results (with a fixed seed) is a nicer UX in my opinion, so picking a different (but still fixed) seed would be my preference

@ctsk
Copy link
Contributor Author

ctsk commented Apr 11, 2025

Do you think that the hash aggregation should also switch to a fixed seed?

@alamb
Copy link
Contributor

alamb commented Apr 11, 2025

It seems like a reasonable proposal to me

@ctsk ctsk linked a pull request Apr 20, 2025 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants