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

Optimize SeriesGrouper & aggregators.merge #8391

Merged
merged 1 commit into from
Jan 7, 2021

Conversation

phemmer
Copy link
Contributor

@phemmer phemmer commented Nov 11, 2020

This introduces some optimizations for use with the aggregators.merge plugin. In my current usage of telegraf, it's heavily bottle-necking on this plugin.


The previous implementation of SeriesGrouper required breaking a metric object apart into its constituents, converting tags and keys into unoptimized maps, only to have it put them back together into another metric object. This resulted in a significant performance overhead. This overhead was further compounded when the number of fields was large.

This change adds a new AddMetric method to SeriesGrouper which preserves the metric object and removes the back-and-forth conversion.

Additionlly the method used for calculating the metric's hash was switched to use maphash, which is optimized for this case.


Benchmarks

Before:

BenchmarkMergeOne-16          106012	     11790 ns/op
BenchmarkMergeTwo-16           48529	     24819 ns/op
BenchmarkGroupID-16           780018	      1608 ns/op

After:

BenchmarkMergeOne-16          907093	      1173 ns/op
BenchmarkMergeTwo-16          508321	      2168 ns/op
BenchmarkGroupID-16         11217788	      99.4 ns/op

Note the BenchmarkGroupID isn't a direct apples-to-apples comparison, since the old method had the sort code present, and the new one does not. However earlier in my development, switching just the hash, while keeping all the other code resulted in about a 2x performance increase (831 ns/op).


Also since the vast majority of metrics are likely not to result in any mergers, a further optimization might be to utilize copy-on-write. Meaning use the input metric without copying it, and if a second metric is merged into it, then copy it. However the current change is a good first step, and that can be investigated later.

Required for all PRs:

  • Signed CLA.
  • Associated README.md updated.
  • Has appropriate unit tests.

@phemmer phemmer force-pushed the optimize-series-grouper branch 5 times, most recently from 266b36a to dc0648c Compare November 11, 2020 14:57
@phemmer
Copy link
Contributor Author

phemmer commented Nov 11, 2020

I haven't a clue whats up with the test failure.

macdeps:

github.com/influxdata/telegraf/metric imports
	hash/maphash: malformed module path "hash/maphash": missing dot in first path element

... No it's not

@phemmer phemmer mentioned this pull request Nov 11, 2020
3 tasks
@reimda
Copy link
Contributor

reimda commented Nov 11, 2020

I took a look at the macdeps failure but I haven't figured out yet why it's failing. First I reran it and it failed again. I noticed when rerunning it that macdeps installs go version 1.13.6 when it intends to install the latest go package from homebrew. Maybe 'go mod tidy' fails only on go 1.13.6? (The other CI steps use 1.14 or 1.15.)

@phemmer
Copy link
Contributor Author

phemmer commented Nov 11, 2020

Ah, that would explain it. maphash was introduced in 1.14.

Given that go.mod has the version set to 1.15, should we not even be testing against lower versions?

@reimda
Copy link
Contributor

reimda commented Nov 11, 2020

I just noticed that hash/maphash is new in 1.14 too.
https://golang.org/doc/go1.14#hash/maphash

Telegraf is currently supporting go 1.14 and 1.15 even though go.mod has the version set to 1.15. Fortunately the 1.14 CI steps are working even with the go.mod version set to 1.15.

To fix this failure we'll have to fix the macdeps job to use a newer golang version. I think it's just a matter of updating .circleci/config.yml to run 'brew update' before running 'brew install go'. Could you make that change and try it out in this PR?

@reimda
Copy link
Contributor

reimda commented Nov 11, 2020

It looks like that worked. Unfortunately 'brew update' took over four minutes to run. We may need to look for a faster way to get an up to date golang install, but you don't need to worry about that for this PR. Thanks for your help debugging CI!

@phemmer
Copy link
Contributor Author

phemmer commented Nov 11, 2020

Yay.

We may need to look for a faster way to get an up to date golang install

Maybe it should use the official package/archive. I know I've seen issues on the golang issue tracker where they won't support people using the homebrew package, and tell these people to go reproduce issues using the official package. I'm unfamiliar with the background, but some googling suggests that homebrew does janky things with the package.

@sjwang90 sjwang90 added the feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin label Nov 24, 2020
metric/series_grouper.go Outdated Show resolved Hide resolved
Copy link
Contributor

@srebhan srebhan left a comment

Choose a reason for hiding this comment

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

Code looks good. Maybe we could move the seed init to the NewSeriesGrouper() call? This would get rid of the global variable and generates an individual seed per grouper if I'm not mistaken...

Could we also move the CircleCI fix out here? It's just a minor thing, but nobody will afterwards search this PR for that... :-)

@reimda
Copy link
Contributor

reimda commented Dec 4, 2020

I updated the golang version for mac ci in #8516. Could you remove the 'brew update' from .circleci/config.yml and rebase this PR? Thanks!

@srebhan
Copy link
Contributor

srebhan commented Dec 18, 2020

@phemmer any news?

The previous implementation of SeriesGrouper required breaking a metric object apart into its constituents, converting tags and keys into unoptimized maps, only to have it put them back together into another metric object. This resulted in a significant performance overhead. This overhead was further compounded when the number of fields was large.

This change adds a new AddMetric method to SeriesGrouper which preserves the metric object and removes the back-and-forth conversion.

Additionlly the method used for calculating the metric's hash was switched to use maphash, which is optimized for this case.

----

Benchmarks

Before:

    BenchmarkMergeOne-16          106012	     11790 ns/op
    BenchmarkMergeTwo-16           48529	     24819 ns/op
    BenchmarkGroupID-16           780018	      1608 ns/op

After:

    BenchmarkMergeOne-16          907093	      1173 ns/op
    BenchmarkMergeTwo-16          508321	      2168 ns/op
    BenchmarkGroupID-16         11217788	      99.4 ns/op
@phemmer
Copy link
Contributor Author

phemmer commented Dec 21, 2020

Updated to address comments.

Copy link
Contributor

@srebhan srebhan left a comment

Choose a reason for hiding this comment

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

Looks good to me.

@srebhan srebhan requested a review from ssoroka December 23, 2020 21:59
@srebhan srebhan added the ready for final review This pull request has been reviewed and/or tested by multiple users and is ready for a final review. label Jan 6, 2021
@ssoroka
Copy link
Contributor

ssoroka commented Jan 7, 2021

Great work, thanks!

@ssoroka ssoroka merged commit 910b726 into influxdata:master Jan 7, 2021
ssoroka pushed a commit that referenced this pull request Jan 27, 2021
The previous implementation of SeriesGrouper required breaking a metric object apart into its constituents, converting tags and keys into unoptimized maps, only to have it put them back together into another metric object. This resulted in a significant performance overhead. This overhead was further compounded when the number of fields was large.

This change adds a new AddMetric method to SeriesGrouper which preserves the metric object and removes the back-and-forth conversion.

Additionlly the method used for calculating the metric's hash was switched to use maphash, which is optimized for this case.

----

Benchmarks

Before:

    BenchmarkMergeOne-16          106012	     11790 ns/op
    BenchmarkMergeTwo-16           48529	     24819 ns/op
    BenchmarkGroupID-16           780018	      1608 ns/op

After:

    BenchmarkMergeOne-16          907093	      1173 ns/op
    BenchmarkMergeTwo-16          508321	      2168 ns/op
    BenchmarkGroupID-16         11217788	      99.4 ns/op

(cherry picked from commit 910b726)
arstercz pushed a commit to arstercz/telegraf that referenced this pull request Mar 5, 2023
The previous implementation of SeriesGrouper required breaking a metric object apart into its constituents, converting tags and keys into unoptimized maps, only to have it put them back together into another metric object. This resulted in a significant performance overhead. This overhead was further compounded when the number of fields was large.

This change adds a new AddMetric method to SeriesGrouper which preserves the metric object and removes the back-and-forth conversion.

Additionlly the method used for calculating the metric's hash was switched to use maphash, which is optimized for this case.

----

Benchmarks

Before:

    BenchmarkMergeOne-16          106012	     11790 ns/op
    BenchmarkMergeTwo-16           48529	     24819 ns/op
    BenchmarkGroupID-16           780018	      1608 ns/op

After:

    BenchmarkMergeOne-16          907093	      1173 ns/op
    BenchmarkMergeTwo-16          508321	      2168 ns/op
    BenchmarkGroupID-16         11217788	      99.4 ns/op
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin ready for final review This pull request has been reviewed and/or tested by multiple users and is ready for a final review.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants