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

ring.DoBatch with goroutine pool #431

Merged
merged 6 commits into from
Nov 14, 2023
Merged

ring.DoBatch with goroutine pool #431

merged 6 commits into from
Nov 14, 2023

Conversation

colega
Copy link
Contributor

@colega colega commented Nov 13, 2023

This adds an option to ring.DoBatch to run the callbacks through a function provided that could make use of a goroutine pool (with concurrency.ReusableGoroutinesPool as an implementation).

This should help in Mimir's distributors to avoid extra stack allocation on every new goroutine created to perform a push to an ingester.

Since there was a recent change to DoBatch that added an isClientError param, I made a more generic version DoBatchWithOptions. I also made the cleanup optional, since I see it's
not so common to pass it down.

With this change, I also marked as deprecated the default ring.DoBatch version.

I did two benchmark versions, showing some imrovement in CPU time on the pooled version:

$ benchstat -col /go bench.txt
goos: linux
goarch: amd64
pkg: github.com/grafana/dskit/ring
cpu: 11th Gen Intel(R) Core(TM) i7-11700K @ 3.60GHz
                 │   default    │  concurrency.NonBlockingWorkerPool   │
                 │    sec/op    │    sec/op     vs base                │
Batch10x100-16     69.04µ ±  0%   64.53µ ±  0%   -6.53% (p=0.000 n=20)
Batch100x100-16    131.6µ ±  1%   123.8µ ±  1%   -5.94% (p=0.000 n=20)
Batch100x1000-16   2.088m ± 30%   1.146m ± 39%  -45.12% (p=0.001 n=20)
geomean            266.7µ         209.2µ        -21.57%

This adds an option to `ring.DoBatch` to run the callbacks through a
function provided that could make use of a goroutine pool (with
concurrency.ReusableGoroutinesPool as an implementation).

This should help in Mimir's distributors to avoid extra stack allocation
on every new goroutine created to perform a push to an ingester.

Since there was a recent change to `DoBatch` that added an
`isClientError` param, I made a more generic version
`DoBatchWithOptions`. I also made the cleanup optional, since I see it's
not so common to pass it down.

I did two benchmark versions, showing some imrovement in CPU time on the
pooled version:

$ benchstat -col /go bench.txt
goos: linux
goarch: amd64
pkg: github.com/grafana/dskit/ring
cpu: 11th Gen Intel(R) Core(TM) i7-11700K @ 3.60GHz
                 │   default    │  concurrency.NonBlockingWorkerPool   │
                 │    sec/op    │    sec/op     vs base                │
Batch10x100-16     69.04µ ±  0%   64.53µ ±  0%   -6.53% (p=0.000 n=20)
Batch100x100-16    131.6µ ±  1%   123.8µ ±  1%   -5.94% (p=0.000 n=20)
Batch100x1000-16   2.088m ± 30%   1.146m ± 39%  -45.12% (p=0.001 n=20)
geomean            266.7µ         209.2µ        -21.57%

Signed-off-by: Oleg Zaytsev <mail@olegzaytsev.com>
@colega colega force-pushed the do-batch-with-goroutine-pool branch from 023a0a4 to e1385b9 Compare November 13, 2023 16:21
@colega colega marked this pull request as ready for review November 13, 2023 16:21
@duricanikolic
Copy link
Contributor

Could you please update CHANGELOG.md and write the improtant changes this PR brings:

  • ring.DoBatchWithOptions() has been added.
  • ring.DoBatch() and ring.DoBatchWithClientError() have been deprecated.

ring/batch.go Outdated Show resolved Hide resolved
//
// Not implemented as a method on Ring, so we can test separately.
func DoBatchWithClientError(ctx context.Context, op Operation, r ReadRing, keys []uint32, callback func(InstanceDesc, []int) error, cleanup func(), isClientError func(error) bool) error {
func DoBatchWithOptions(ctx context.Context, op Operation, r ReadRing, keys []uint32, callback func(InstanceDesc, []int) error, o DoBatchOptions) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you remove DoBatchWithClientError()?
I would put it back and possibly mark it as deprecated, since it will now be part of r264.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't do server in dskit. We're already very generous at leaving the DoBatch with the old signature: I didn't change it just to make the update path easier for codebases with tangled dependencies.

I don't see any need to leave DoBatchWithClientError here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Then we should replace DoBatchWithClientError with DoBatchWithOptions in CHANGELOG too, and mention both PRs there.
The usage of DoBatchWithClientError should be removed from Mimir once the vendored dskit is updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changelog updated. We'll update Mimir once this is vendored there, of course.

// NewReusableGoroutinesPool creates a new worker pool with the given size.
// These workers will run the workloads passed through Go() calls.
// If all workers are busy, Go() will spawn a new goroutine to run the workload.
func NewReusableGoroutinesPool(size int) *ReusableGoroutinesPool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we plan to add a CLI flag for specifying the pool size in mimir?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, there will be a CLI flag just like there's one in the dskit/server for grpc workers.

Signed-off-by: Oleg Zaytsev <mail@olegzaytsev.com>
@krajorama krajorama self-requested a review November 14, 2023 08:50
Signed-off-by: Oleg Zaytsev <mail@olegzaytsev.com>
@colega
Copy link
Contributor Author

colega commented Nov 14, 2023

I have updated the CHANGELOG.md, however since we don't do releases, and changelog is sorted by section type (CHANGE/FEATURE/ENHANCEMENT/BUGFIX) instead of the date, I think it's quite useless.

Copy link
Contributor

@duricanikolic duricanikolic left a comment

Choose a reason for hiding this comment

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

Thank you for addressing my comments.
LGTM

Signed-off-by: Oleg Zaytsev <mail@olegzaytsev.com>
CHANGELOG.md Outdated Show resolved Hide resolved
ring/ring_test.go Outdated Show resolved Hide resolved
concurrency/worker_test.go Show resolved Hide resolved
colega and others added 2 commits November 14, 2023 13:14
Co-authored-by: George Krajcsovits <krajorama@users.noreply.github.com>
Co-authored-by: George Krajcsovits <krajorama@users.noreply.github.com>
Copy link
Contributor

@krajorama krajorama left a comment

Choose a reason for hiding this comment

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

Not an expert on goroutines, but LGTM

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.

None yet

3 participants