Skip to content
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

Memberlist: Optimise receive path by not cloning current ring state. #76

Merged
merged 14 commits into from
Nov 26, 2021

Conversation

stevesg
Copy link
Contributor

@stevesg stevesg commented Nov 23, 2021

What this PR does:

When memberlist is used as the backing store for the ring, processing updates
can be costly if the ring state is very large. This is partly due to the ring
state being cloned upon the arrival of every message. It is believed that this
can be avoided. Additionally, the reduced memory allocations will reduce the
amount of work required by the GC.

The included benchmark shows a good improvement:

Command:

dskit/bench$ go test -v -bench BenchmarkMemberlistReceiveWithRingDesc -benchmem -cpu 6
name                             old time/op    new time/op    delta
MemberlistReceiveWithRingDesc-6    1.76ms ± 5%    1.01ms ± 6%  -42.32%  (p=0.000 n=10+9)

name                             old alloc/op   new alloc/op   delta
MemberlistReceiveWithRingDesc-6     566kB ± 0%     254kB ± 0%  -55.09%  (p=0.000 n=9+10)

name                             old allocs/op  new allocs/op  delta
MemberlistReceiveWithRingDesc-6     1.86k ± 0%     0.64k ± 0%  -65.83%  (p=0.000 n=10+10)

(Edit: Updated results to take into account adding tokens to benchmark)

Which issue(s) this PR fixes:

Fixes N/A

Checklist

  • Tests updated
  • CHANGELOG.md updated - the order of entries should be [CHANGE], [FEATURE], [ENHANCEMENT], [BUGFIX]

The memberlist is used as the backing store for the ring, processing updates
can be costly if the ring state is very large. This is partly due to the ring
state being cloned upon the arrival of every message. It is believed that this
can be avoided. Additionally, the reduced memory allocations will reduce the
amount of work required by the GC.

The included benchmark shows a good improvement:

Command:
```
dskit/bench$ go test -v -bench BenchmarkMemberlistReceiveWithRingDesc -benchmem -cpu 6
```

```
BenchmarkMemberlistReceiveWithRingDesc-6   	    1138	    975938 ns/op	  551572 B/op	    1264 allocs/op
```

```
BenchmarkMemberlistReceiveWithRingDesc-6   	    3580	    319163 ns/op	  239799 B/op	      37 allocs/op
```
bench/ring_memberlist_test.go Outdated Show resolved Hide resolved
kv/memberlist/memberlist_client.go Show resolved Hide resolved
@pstibrany
Copy link
Member

I'm getting race conditions when running memberlist tests with -race.

@stevesg
Copy link
Contributor Author

stevesg commented Nov 23, 2021

Me too, looking into it.

This is necessary because the Mergeable implementors are allowed to reference
the source state in the returned changes. For example, in `ring.Desc`, the
`Tokens` slice can be shared and is not deep copied. For safety, we can just
clone the whole thing, given the changes are typically small.

```
BenchmarkMemberlistReceiveWithRingDesc-6   	    3229	    324260 ns/op	  239825 B/op	      37 allocs/op
```
@stevesg
Copy link
Contributor Author

stevesg commented Nov 23, 2021

I've added a fix to Clone() the change while holding the lock before returning them. I think this is necessary because the Mergeable implementors are allowed to reference the source state in the returned changes. For example, in ring.Desc, the Tokens slice can be shared and is not deep copied. For safety, we can just clone the whole thing, given the changes are typically small.

The performance appears to be unchanged.

(Edit: Removed outdated benchmark numbers)

@stevesg stevesg marked this pull request as ready for review November 23, 2021 17:29
kv/memberlist/memberlist_client.go Outdated Show resolved Hide resolved
kv/memberlist/memberlist_client.go Show resolved Hide resolved
Copy link
Contributor

@pracucci pracucci left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a very limited knowledge of our memberlist implementation, so don't trust my review as much as Peter's one. To my understanding LGTM.

CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
bench/ring_memberlist_test.go Outdated Show resolved Hide resolved
kv/memberlist/memberlist_client.go Show resolved Hide resolved
kv/memberlist/memberlist_client.go Show resolved Hide resolved
Copy link
Contributor

@replay replay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find!

LGTM, I just added some suggestions

Copy link
Contributor

@pracucci pracucci left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It still LGTM after your latest changes. However, keep in mind my limited knowledge of our memberlist implementation.

Copy link
Member

@pstibrany pstibrany left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job, thanks!

kv/memberlist/mergeable.go Outdated Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants