Skip to content

Improve performance of Ring.shuffleShard()#281

Merged
charleskorn merged 18 commits intomainfrom
charleskorn/improve-shuffleshard-performance
Apr 12, 2023
Merged

Improve performance of Ring.shuffleShard()#281
charleskorn merged 18 commits intomainfrom
charleskorn/improve-shuffleshard-performance

Conversation

@charleskorn
Copy link
Copy Markdown
Contributor

@charleskorn charleskorn commented Apr 4, 2023

What this PR does:

This PR improves the performance of Ring.shuffleShard(), and specifically the evaluation of the list of all tokens in the shard. It also introduces a new benchmark for shuffle sharding, BenchmarkRing_ShuffleShard_LargeShardSize, which better mirrors the worst-case parameters we see in production Mimir cells.

There are two main optimisations:

  • to compute the per-zone lists of tokens, use a loser tree rather than a heap to merge per-host lists of tokens
  • to compute the overall list of tokens, merge the per-zone lists of tokens rather than merging the per-host lists of tokens, and use a simplified merge algorithm to perform this merge step (benchmarks show that it performs better than using either a heap or a loser tree when merging such a small number of lists)

The loser tree implementation is based on @bboreham's implementation in Loki, which we are relicensing as part of this PR. I've modified it to work on slices directly, rather than Sequence interfaces, as this improved overall shuffle shard computation performance by ~15%, and incorporated the fix in grafana/loki#9057.

Benchmark results:

goos: darwin
goarch: arm64
pkg: github.com/grafana/dskit/ring
                                                                          │  before.txt   │             final.txt              │
                                                                          │    sec/op     │   sec/op     vs base               │
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_1,_shard_size_=_3-10       28.02µ ±  1%   17.82µ ± 0%  -36.41% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_1,_shard_size_=_10-10     105.36µ ±  0%   51.41µ ± 0%  -51.20% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_1,_shard_size_=_30-10      418.0µ ±  1%   159.6µ ± 0%  -61.81% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_3,_shard_size_=_3-10       48.00µ ±  1%   45.07µ ± 0%   -6.10% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_3,_shard_size_=_10-10     134.43µ ±  0%   86.19µ ± 1%  -35.89% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_3,_shard_size_=_30-10      381.7µ ±  1%   202.3µ ± 6%  -46.99% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_1,_shard_size_=_3-10      29.08µ ±  2%   18.31µ ± 2%  -37.04% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_1,_shard_size_=_10-10    106.16µ ±  2%   52.62µ ± 0%  -50.43% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_1,_shard_size_=_30-10     423.7µ ±  1%   161.1µ ± 1%  -61.99% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_3,_shard_size_=_3-10      48.99µ ±  1%   45.88µ ± 0%   -6.36% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_3,_shard_size_=_10-10    136.33µ ±  3%   87.78µ ± 1%  -35.61% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_3,_shard_size_=_30-10     385.8µ ±  0%   203.4µ ± 0%  -47.28% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_1,_shard_size_=_3-10     26.77µ ±  2%   16.46µ ± 0%  -38.50% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_1,_shard_size_=_10-10   103.64µ ±  0%   49.62µ ± 0%  -52.13% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_1,_shard_size_=_30-10    418.8µ ± 32%   157.1µ ± 0%  -62.48% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_3,_shard_size_=_3-10     45.33µ ±  0%   42.24µ ± 1%   -6.82% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_3,_shard_size_=_10-10   131.38µ ±  1%   81.63µ ± 1%  -37.87% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_3,_shard_size_=_30-10    378.6µ ±  0%   194.9µ ± 1%  -48.51% (p=0.002 n=6)
Ring_ShuffleShard_512Tokens-10                                               286.6µ ±  0%   170.8µ ± 0%  -40.40% (p=0.002 n=6)
Ring_ShuffleShard_LargeShardSize-10                                         22.624m ±  0%   7.956m ± 1%  -64.83% (p=0.002 n=6)
geomean                                                                      162.9µ         91.56µ       -43.81%

                                                                          │  before.txt   │              final.txt              │
                                                                          │     B/op      │     B/op      vs base               │
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_1,_shard_size_=_3-10      11.039Ki ± 0%   9.391Ki ± 0%  -14.93% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_1,_shard_size_=_10-10      21.12Ki ± 0%   15.51Ki ± 0%  -26.56% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_1,_shard_size_=_30-10      51.46Ki ± 0%   34.21Ki ± 0%  -33.52% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_3,_shard_size_=_3-10       21.61Ki ± 0%   21.55Ki ± 0%   -0.25% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_3,_shard_size_=_10-10      33.49Ki ± 0%   33.03Ki ± 0%   -1.38% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_3,_shard_size_=_30-10      62.54Ki ± 0%   61.41Ki ± 0%   -1.79% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_1,_shard_size_=_3-10     11.039Ki ± 0%   9.391Ki ± 0%  -14.93% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_1,_shard_size_=_10-10     21.12Ki ± 0%   15.51Ki ± 0%  -26.56% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_1,_shard_size_=_30-10     51.46Ki ± 0%   34.21Ki ± 0%  -33.52% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_3,_shard_size_=_3-10      21.61Ki ± 0%   21.55Ki ± 0%   -0.25% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_3,_shard_size_=_10-10     33.49Ki ± 0%   33.03Ki ± 0%   -1.37% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_3,_shard_size_=_30-10     62.53Ki ± 0%   61.42Ki ± 0%   -1.79% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_1,_shard_size_=_3-10    11.039Ki ± 0%   9.391Ki ± 0%  -14.93% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_1,_shard_size_=_10-10    21.12Ki ± 0%   15.51Ki ± 0%  -26.56% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_1,_shard_size_=_30-10    51.46Ki ± 0%   34.21Ki ± 0%  -33.52% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_3,_shard_size_=_3-10     21.61Ki ± 0%   21.55Ki ± 0%   -0.25% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_3,_shard_size_=_10-10    33.49Ki ± 0%   33.03Ki ± 0%   -1.37% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_3,_shard_size_=_30-10    62.54Ki ± 0%   61.42Ki ± 0%   -1.78% (p=0.002 n=6)
Ring_ShuffleShard_512Tokens-10                                               56.92Ki ± 0%   56.56Ki ± 0%   -0.63% (p=0.002 n=6)
Ring_ShuffleShard_LargeShardSize-10                                          1.203Mi ± 0%   1.193Mi ± 0%   -0.80% (p=0.002 n=6)
geomean                                                                      35.69Ki        31.10Ki       -12.86%

                                                                          │ before.txt  │             final.txt             │
                                                                          │  allocs/op  │ allocs/op   vs base               │
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_1,_shard_size_=_3-10       32.00 ± 0%   22.00 ± 0%  -31.25% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_1,_shard_size_=_10-10      62.00 ± 0%   31.00 ± 0%  -50.00% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_1,_shard_size_=_30-10     143.00 ± 0%   52.00 ± 0%  -63.64% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_3,_shard_size_=_3-10       44.00 ± 0%   37.00 ± 0%  -15.91% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_3,_shard_size_=_10-10      86.00 ± 0%   52.00 ± 0%  -39.53% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_50,_num_zones_=_3,_shard_size_=_30-10     164.00 ± 0%   76.00 ± 0%  -53.66% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_1,_shard_size_=_3-10      32.00 ± 0%   22.00 ± 0%  -31.25% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_1,_shard_size_=_10-10     62.00 ± 0%   31.00 ± 0%  -50.00% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_1,_shard_size_=_30-10    143.00 ± 0%   52.00 ± 0%  -63.64% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_3,_shard_size_=_3-10      44.00 ± 0%   37.00 ± 0%  -15.91% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_3,_shard_size_=_10-10     86.00 ± 0%   52.00 ± 0%  -39.53% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_100,_num_zones_=_3,_shard_size_=_30-10    164.00 ± 0%   76.00 ± 0%  -53.66% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_1,_shard_size_=_3-10     32.00 ± 0%   22.00 ± 0%  -31.25% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_1,_shard_size_=_10-10    62.00 ± 0%   31.00 ± 0%  -50.00% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_1,_shard_size_=_30-10   143.00 ± 0%   52.00 ± 0%  -63.64% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_3,_shard_size_=_3-10     44.00 ± 0%   37.00 ± 0%  -15.91% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_3,_shard_size_=_10-10    86.00 ± 0%   52.00 ± 0%  -39.53% (p=0.002 n=6)
Ring_ShuffleShard/num_instances_=_1000,_num_zones_=_3,_shard_size_=_30-10   164.00 ± 0%   76.00 ± 0%  -53.66% (p=0.002 n=6)
Ring_ShuffleShard_512Tokens-10                                               74.00 ± 0%   49.00 ± 0%  -33.78% (p=0.002 n=6)
Ring_ShuffleShard_LargeShardSize-10                                         1134.0 ± 0%   326.0 ± 0%  -71.25% (p=0.002 n=6)
geomean                                                                      85.71        46.49       -45.76%

Which issue(s) this PR fixes:

(none)

Checklist

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

}

if !haveSeenGroupWithRemainingToken {
return merged
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note to reviewers: this should never happen, but I couldn't think of a nicer way to handle this - open to suggestions.

@charleskorn charleskorn marked this pull request as ready for review April 4, 2023 07:18
@charleskorn charleskorn force-pushed the charleskorn/improve-shuffleshard-performance branch from 39c479a to 60e2e2a Compare April 5, 2023 03:28
@charleskorn charleskorn marked this pull request as draft April 5, 2023 06:04
@charleskorn charleskorn marked this pull request as ready for review April 11, 2023 04:40
Copy link
Copy Markdown
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'm assuming the loser tree implementation is correct, given it's getting copied from Loki and I also assume the fuzz test has been run manually given it doesn't run in CI. Based on these assumptions, I've checked the rest of the code and LGTM (modulo a couple of nits).

@charleskorn
Copy link
Copy Markdown
Contributor Author

charleskorn commented Apr 12, 2023

I'm assuming the loser tree implementation is correct, given it's getting copied from Loki

I have modified it slightly to not use an interface, but the tests exercise the same test cases, so I believe this hasn't broken anything.

I also assume the fuzz test has been run manually given it doesn't run in CI.

Yep.

Copy link
Copy Markdown
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.

LGTM, thanks!

@charleskorn charleskorn merged commit 86cce08 into main Apr 12, 2023
@charleskorn charleskorn deleted the charleskorn/improve-shuffleshard-performance branch April 12, 2023 07:20
charleskorn added a commit to grafana/mimir that referenced this pull request Apr 12, 2023
charleskorn added a commit to grafana/mimir that referenced this pull request Apr 12, 2023
* Upgrade to latest dskit version.

This brings in grafana/dskit#280 and
grafana/dskit#281.

* Add changelog entry.
charleskorn pushed a commit that referenced this pull request Aug 3, 2023
Upgrades to a newer version (v1.53.0) of google.golang.org/grpc that includes breaking changes to the resolver.Target type (see Cleanup usages of resolver.Target.Endpoint grpc/grpc-go#5796)
Upgrade from v2.4.0 to v.4.0.0 of https://github.com/sercand/kuberesolver which is compatible to grpc to v1.53.0.
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.

2 participants