-
Notifications
You must be signed in to change notification settings - Fork 17
perf: remove hashMap for optimized peer selection #80
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
perf: remove hashMap for optimized peer selection #80
Conversation
Replace separate keyHashes []uint32 and hashMap map[uint32]string with a single segments []segment struct slice. This eliminates the map lookup overhead in the hot path (Get, findSegmentOwner). Benchmark results show 7-22% performance improvement: | Benchmark | Old (ns/op) | New (ns/op) | Speedup | |-------------------------------|-------------|-------------|--------------| | Get/segs50-shards8 | 37.99 | 34.47 | 9.3% faster | | Get/segs50-shards32 | 40.66 | 37.41 | 8.0% faster | | Get/segs50-shards512 | 71.94 | 65.40 | 9.1% faster | | GetReplicated/shards8-reps2 | 174.2 | 161.8 | 7.1% faster | | GetReplicated/shards512-reps2 | 242.9 | 203.8 | 16.1% faster | | GetReplicated/shards8-reps8 | 1061.0 | 823.9 | 22.4% faster | Geometric mean: 11.5% faster Memory allocations: unchanged
043796f to
1488f8d
Compare
|
@dfinkel can you please take a look at this PR? I have mentioned the change in the description, which is a perf enhancement |
|
Thanks for sending this PR! Sorry about the delay. (We were on vacation for the end of the year) This does look like a nice improvement, and the code looks good at a first glance, I'll make a closer pass once the tests have passed. By the way, it's helpful to link to or reference the PR and/or commit from upstream when porting patches or reimplementing them. |
|
sure @dfinkel |
dfinkel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
Just a couple minor nits.
consistenthash/map_rangefunc.go
Outdated
| if a.hash < b.hash { | ||
| return -1 | ||
| } | ||
| if a.hash > b.hash { | ||
| return 1 | ||
| } | ||
| return 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be simplified by using cmp.Compare
slices.SortFunc(m.segments, func(a, b segment) int { return cmp.Compare(a.hash,b.hash)}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| hashToOwner := make(map[uint32]string, len(m.segments)+len(ownerKeys)*m.segsPerKey) | ||
| for _, seg := range m.segments { | ||
| hashToOwner[seg.hash] = seg.owner | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a comment saying that Add() is called several orders of magnitude less often than Get and GetReplicated, since hashrings should change slowly, so it's beneficial to trade a smaller Map against doing a bit more work when calling Add.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added the comment. Kept the wording quite similar to what you mentioned. Hope that's fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works perfectly 🙂
dfinkel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thanks!
| hashToOwner := make(map[uint32]string, len(m.segments)+len(ownerKeys)*m.segsPerKey) | ||
| for _, seg := range m.segments { | ||
| hashToOwner[seg.hash] = seg.owner | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works perfectly 🙂
|
I've tagged v1.4.1 with this change: |
Summary
This PR optimizes the consistent hash implementation by replacing the separate
keyHashes []uint32slice andhashMap map[uint32]stringwith a singlesegments []segmentstruct slice. This eliminates the map lookup overhead in the hot path (Get(),findSegmentOwner()).Changes
segmentstruct combininghash uint32andowner stringkeyHashesandhashMapwithsegments []segmentfindSegmentOwner()to access segments directly (the key optimization)Add()to use a temporary map during insertion for collision handlingBenchmark Comparison
Memory allocations remain unchanged.
Motivation
Inspired by a similar optimization in groupcache (groupcache/groupcache-go#22). The key insight is that storing hash and owner together in a struct slice provides: