Skip to content

Add benchmark tests for StatsReport implementations - #91

Merged
SokolAndrey merged 11 commits into
uber-java:masterfrom
SokolAndrey:as/reporters_benchmarks
Mar 11, 2021
Merged

Add benchmark tests for StatsReport implementations#91
SokolAndrey merged 11 commits into
uber-java:masterfrom
SokolAndrey:as/reporters_benchmarks

Conversation

@SokolAndrey

@SokolAndreySokolAndrey commented Jan 31, 2021

Copy link
Copy Markdown
Collaborator

Adds benchmark tests for all StatsReporter implementations.

Comment threadcore/src/main/java/com/uber/m3/jmh/AbstractReporterBenchmark.java Outdated
@SokolAndreySokolAndrey linked an issue Feb 1, 2021 that may be closed by this pull request

@andrewmains12andrewmains12 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Lgtm in general, though definitely worth getting a pass from @alexeykudinkin since he has more Java perf experience than me.

Comment threadprometheus/README.md Outdated
Benchmark Mode Cnt Score Error Units
PrometheusReporterBenchmark.reportCounterBenchmark thrpt 10 243.828 ± 5.643 ops/ms
PrometheusReporterBenchmark.reportGaugeBenchmark thrpt 10 247.080 ± 5.012 ops/ms
PrometheusReporterBenchmark.reportHistogramDurationSamplesBenchmark thrpt 10 0.694 ± 0.114 ops/ms

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Dang, thoughts on what's going on with this one? (obviously we could do the optimizations themselves in a separate PR).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Heavy memory churn

@andrewmains12andrewmains12Feb 2, 2021

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

^ sounds plausible, looking forward to the optimizations there in the next pass.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

this is the worst part of this prom implementation, which also mimics go-lang lib. The issue here is the difference between implementations of m3 and prom histograms. M3 uses counters to implement buckets and then reports each bucket separately. While prom updates all the buckets at the same time in a for-cycle.

 public void reportHistogramDurationSamples(
String name,
Map<String, String> tags,
Buckets buckets,
Duration bucketLowerBound,
Duration bucketUpperBound,
long samples
) {
...
for (int i = 0; i < samples; i++) {
histogram.observe(bucketUpperBoundValue);
}
}

What for m3 is just a one-shot report for particular bucket, for prometheus is a for-cycle over all the buckets. So what we have here is something like a O(CN), where C is the number of samples, I used an incremental var from 0 to max long, and N is the number of buckets. I think using incremental value in range [0; MAX_LONG] is a bit of an overkill and not realistic. But there is definitely a huge room for improvements.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Also long samples is the delta for a bucket value between reporting intervals (which is configurable on client side).
I run the tests with an incremental value in range [0; 50] and the results are following:

Benchmark Mode Cnt Score Error Units
PrometheusReporterBenchmark.reportCounterBenchmark thrpt 10 228.800 ± 39.789 ops/ms
PrometheusReporterBenchmark.reportGaugeBenchmark thrpt 10 220.869 ± 22.208 ops/ms
PrometheusReporterBenchmark.reportHistogramDurationSamplesBenchmark thrpt 10 222.604 ± 22.569 ops/ms
PrometheusReporterBenchmark.reportHistogramValueSamplesBenchmark thrpt 10 209.727 ± 29.474 ops/ms
PrometheusReporterBenchmark.reportTimerBenchmark thrpt 10 24.976 ± 4.750 ops/ms

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@SokolAndrey this has been addressed in M3 fairly recently in #74. Prior to that it was also churning CPU traversing empty arrays.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

#93 created an issue to address that.

Comment threadcore/src/jmh/java/com/uber/m3/tally/AbstractReporterBenchmark.java Outdated
Comment threadm3/README.md Outdated
@@ -0,0 +1,8 @@
# JMH Benchmark Tests results

Benchmark Mode Cnt Score Error Units

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Btw @SokolAndrey do we have a sense of what the allocation profiles look like here? cc. @alexeykudinkin for approaches on getting that (I think Andrei mentioned utilizing the IntelliJ profiler there, which might work).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No need for external profilers, you can use JMH internal profilers for that.

https://stackoverflow.com/a/22642703

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ooh nice that's much better, thanks!

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

added

Comment threadcore/build.gradle
@alexeykudinkin

Copy link
Copy Markdown
Contributor

Awesome work @SokolAndrey!

It would be great to add benchmark for the whole suite (Processor + Reporter) as well (we can do it separately if you prefer).

@SokolAndrey

SokolAndrey commented Feb 2, 2021

Copy link
Copy Markdown
CollaboratorAuthor

Awesome work @SokolAndrey!

It would be great to add benchmark for the whole suite (Processor + Reporter) as well (we can do it separately if you prefer).

Good idea! I guess it should fit in this PR

@SokolAndrey

SokolAndrey commented Feb 9, 2021

Copy link
Copy Markdown
CollaboratorAuthor

Awesome work @SokolAndrey!
It would be great to add benchmark for the whole suite (Processor + Reporter) as well (we can do it separately if you prefer).

Good idea! I guess it should fit in this PR

I guess it could be added as a separate PR. The issue is all the implementation of Reportable#report exit earlier if the reporting delta is 0. This makes benchmarking quite complicated because there must be something that triggers the metric change without affecting the measurement.

@andrewmains12andrewmains12 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Lgtm! Not sure if @alexeykudinkin has anything pending here; I'd say let's merge it if not.

Some followup things I'd want to see:

  • Possible benchmarks on adding new metric ids, not just incrementing old ones--users in general should optimize away from doing too much of that in hot paths, but it's not always avoidable, and we should make sure we don't die in that case.
  • Any optimizations, of course :) (I haven't read deeply through the results yet, but I expect there will be optimizations to be made).

@alexeykudinkinalexeykudinkin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left a few comments, but otherwise LGTM

PrometheusReporterBenchmark.reportHistogramDurationSamplesBenchmark:·gc.count thrpt 10 2.000 counts
PrometheusReporterBenchmark.reportHistogramDurationSamplesBenchmark:·gc.time thrpt 10 4.000 ms
PrometheusReporterBenchmark.reportHistogramValueSamplesBenchmark thrpt 10 0.777 ± 0.120 ops/ms
PrometheusReporterBenchmark.reportHistogramValueSamplesBenchmark:·gc.alloc.rate thrpt 10 4.355 ± 0.653 MB/sec

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Something is off in this benchmark:

  • It has substantially less alloc rate than counters (which is unlikely)
  • It has only 2 GCs occurring

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

i think it makes sense: the benchmark test has a long counter which goes up to Long.MAX_VALUE and with current implementation of histogram the tests keeps busy iterating over this huge long value and as a result there are not many allocations and not that many GCs happening. Need to fix this reportHistogram* methods

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the tests keeps busy iterating over this huge long value and as a result there are not many allocations and not that many GCs happening

Not sure I get why we're iterating up to Long.MAX_VALUE in the test; are we testing the right thing here?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Sorry for the long reply. Need to configure notifications.
my reasoning there is:

  1. we use long type as an input param => the value can potentially go up to the Long.MAX_VALUE (even though highly unlikely);
  2. the value resets after each report interval, which is configurable on the client side. From what I found in the usages the report interval varies from 1s to few minutes;
  3. Other implementations do not depend on the value range;
  4. I couldn't reason for any other particular value as a threshold, so why not go up to the max;
  5. It's a good indicator we need to improve it :)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for explanation!

Other implementations do not depend on the value range;

I didn't realize that this one did haha; definitely agreed that we should figure out how to not do that here. Will look more into that part of the implementation to develop a more informed opinion.

@@ -0,0 +1,52 @@
Benchmark Mode Cnt Score Error Units
M3ReporterBenchmark.reportCounterBenchmark thrpt 10 392.408 ± 172.561 ops/ms

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's also capture other benchmark values (other than GC related)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

JMH only allows to run one profiler at a time. We can change the gradle task and pass different profilers as a param. But I'd prefer doing it incrementally in another PR

@alexeykudinkin

Copy link
Copy Markdown
Contributor

@SokolAndrey this could be 2 separate tests:

  1. With mostly static metric being reported (no updates)
  2. With metric updated once per report tick (before the report we increment)

@SokolAndrey
SokolAndrey merged commit 5709ef5 into uber-java:masterMar 11, 2021
@SokolAndrey
SokolAndrey deleted the as/reporters_benchmarks branch March 11, 2021 18:18
sairamch04 pushed a commit to sairamch04/tally that referenced this pull request Feb 5, 2023
Sign up for freeto 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.

Add benchmark tests for all Reporter implementations

3 participants

@SokolAndrey@alexeykudinkin@andrewmains12