-
Notifications
You must be signed in to change notification settings - Fork 282
chore(metrics): add benchmark for metrics #2106
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
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # Metrics API Benchmarks | ||
|
|
||
| This directory contains [benchmark-ips](https://github.com/evanphx/benchmark-ips) benchmarks for the OpenTelemetry Ruby Metrics API and SDK. They cover no-op API recording, SDK instrument recording, attribute cardinality, views, exemplar filters, exemplar reservoirs, and aggregations. | ||
|
|
||
| ## Running the Benchmarks | ||
|
|
||
| Run from the `metrics_api/` directory, adding each sibling gem's `lib/` to the load path: | ||
|
|
||
| ```bash | ||
| bundle exec ruby benchmarks/aggregation_bench.rb | ||
| bundle exec ruby benchmarks/attributes_bench.rb | ||
| bundle exec ruby benchmarks/exemplar_filter_bench.rb | ||
| bundle exec ruby benchmarks/exemplar_reservoir_bench.rb | ||
| bundle exec ruby benchmarks/instrument_bench.rb | ||
| bundle exec ruby benchmarks/noop_instrument_bench.rb | ||
| bundle exec ruby benchmarks/view_bench.rb | ||
| ``` | ||
|
|
||
| ## Benchmark Files | ||
|
|
||
| | File | What it measures | | ||
| | ---- | --------------- | | ||
| | `instrument_bench.rb` | No-op API instruments vs real SDK instruments — all synchronous types (counter, histogram, gauge, up-down counter) | | ||
|
robbkidd marked this conversation as resolved.
Outdated
|
||
| | `noop_instrument_bench.rb` | No-op API instruments only — micro-benchmark of the lightest possible instrumentation layer | | ||
| | `attributes_bench.rb` | How attribute set size (0 / 1 / 3 / 8 keys) affects SDK counter throughput | | ||
| | `view_bench.rb` | Impact of zero, one matching, one non-matching, and three matching registered views | | ||
| | `exemplar_filter_bench.rb` | Exemplar filter cost (`AlwaysOff` / `AlwaysOn` / `TraceBased`) on SDK counter | | ||
| | `exemplar_reservoir_bench.rb` | Exemplar reservoir cost (`Noop` / `SimpleFixedSize` / `AlignedHistogramBucket`) with `AlwaysOn` filter | | ||
| | `aggregation_bench.rb` | Histogram recording throughput across all five aggregations (`Drop`, `Sum`, `LastValue`, `ExplicitBucketHistogram`, `ExponentialBucketHistogram`) | | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| require_relative 'bench_helper' | ||
|
|
||
| def histogram_with_agg(aggregation) | ||
| provider = OpenTelemetry::SDK::Metrics::MeterProvider.new | ||
| provider.add_view('bench.agg.histogram', aggregation: aggregation) | ||
| provider.add_metric_reader(new_reader) | ||
| provider.meter('bench').create_histogram('bench.agg.histogram') | ||
| end | ||
|
|
||
| explicit_hist = histogram_with_agg(Agg::ExplicitBucketHistogram.new) | ||
| exponential_hist = histogram_with_agg(Agg::ExponentialBucketHistogram.new) | ||
| sum_hist = histogram_with_agg(Agg::Sum.new) | ||
| last_value_hist = histogram_with_agg(Agg::LastValue.new) | ||
| drop_hist = histogram_with_agg(Agg::Drop.new) | ||
|
|
||
| Benchmark.ips do |x| | ||
| x.report('histogram ExplicitBucketHistogram') { explicit_hist.record(42) } | ||
| x.report('histogram ExponentialBucketHistogram') { exponential_hist.record(42) } | ||
| x.report('histogram Sum aggregation') { sum_hist.record(42) } | ||
| x.report('histogram LastValue aggregation') { last_value_hist.record(42) } | ||
| x.report('histogram Drop aggregation') { drop_hist.record(42) } | ||
| x.compare! | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| require_relative 'bench_helper' | ||
|
|
||
| ATTRS_NONE = {}.freeze | ||
| ATTRS_SMALL = { 'env' => 'prod' }.freeze | ||
| ATTRS_LARGE = { | ||
| 'http.method' => 'GET', | ||
| 'http.status_code' => 200, | ||
| 'http.route' => '/api/users', | ||
| 'net.host.name' => 'example.com', | ||
| 'net.host.port' => 443, | ||
| 'http.scheme' => 'https', | ||
| 'http.flavor' => '1.1', | ||
| 'http.user_agent' => 'Ruby/3.3' | ||
| }.freeze | ||
|
|
||
| puts "\n#{'=' * 60}" | ||
| puts '= Attribute cardinality (SDK counter)' | ||
| puts '=' * 60 | ||
|
|
||
| card_counter = build_sdk_meter.create_counter('bench.cardinality.counter') | ||
|
|
||
| Benchmark.ips do |x| | ||
| x.report('counter#add (no attrs)') { card_counter.add(1, attributes: ATTRS_NONE) } | ||
| x.report('counter#add (small attrs)') { card_counter.add(1, attributes: ATTRS_SMALL) } | ||
| x.report('counter#add (medium attrs)') { card_counter.add(1, attributes: ATTRS_MEDIUM) } | ||
| x.report('counter#add (large attrs)') { card_counter.add(1, attributes: ATTRS_LARGE) } | ||
| x.compare! | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| require 'benchmark/ips' | ||
| require 'opentelemetry-metrics-api' | ||
| require 'opentelemetry/sdk/metrics' | ||
|
|
||
| OpenTelemetry.logger = Logger.new(File::NULL) | ||
|
|
||
| Agg = OpenTelemetry::SDK::Metrics::Aggregation | ||
| Ex = OpenTelemetry::SDK::Metrics::Exemplar | ||
| Export = OpenTelemetry::SDK::Metrics::Export | ||
|
|
||
| ATTRS_MEDIUM = { 'http.method' => 'GET', 'http.status_code' => 200, 'http.route' => '/api/users' }.freeze | ||
|
robbkidd marked this conversation as resolved.
Outdated
|
||
|
|
||
| def new_reader | ||
| Export::InMemoryMetricPullExporter.new | ||
| end | ||
|
|
||
| def build_sdk_meter(exemplar_filter: nil, views: []) | ||
| provider = OpenTelemetry::SDK::Metrics::MeterProvider.new | ||
| provider.enable_exemplar_filter(exemplar_filter: exemplar_filter) if exemplar_filter | ||
| views.each { |(name, opts)| provider.add_view(name, **opts) } | ||
| provider.add_metric_reader(new_reader) | ||
| provider.meter('bench') | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| require_relative 'bench_helper' | ||
|
|
||
| def counter_with(exemplar_filter:, exemplar_reservoir: nil) | ||
| meter = build_sdk_meter(exemplar_filter: exemplar_filter) | ||
| meter.create_counter('bench.exemplar.counter', exemplar_reservoir: exemplar_reservoir) | ||
| end | ||
|
|
||
| def histogram_with(exemplar_filter:, exemplar_reservoir: nil) | ||
| meter = build_sdk_meter(exemplar_filter: exemplar_filter) | ||
| meter.create_histogram('bench.exemplar.histogram', exemplar_reservoir: exemplar_reservoir) | ||
| end | ||
|
robbkidd marked this conversation as resolved.
Outdated
|
||
|
|
||
| always_off_counter = counter_with(exemplar_filter: Ex::AlwaysOffExemplarFilter) | ||
| always_on_counter = counter_with(exemplar_filter: Ex::AlwaysOnExemplarFilter) | ||
| trace_based_counter = counter_with(exemplar_filter: Ex::TraceBasedExemplarFilter) | ||
|
|
||
| Benchmark.ips do |x| | ||
| x.report('counter#add AlwaysOff filter') { always_off_counter.add(1) } | ||
| x.report('counter#add AlwaysOn filter') { always_on_counter.add(1) } | ||
| x.report('counter#add TraceBased filter') { trace_based_counter.add(1) } | ||
| x.compare! | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| require_relative 'bench_helper' | ||
|
|
||
| def counter_with(exemplar_filter:, exemplar_reservoir: nil) | ||
| meter = build_sdk_meter(exemplar_filter: exemplar_filter) | ||
| meter.create_counter('bench.exemplar.counter', exemplar_reservoir: exemplar_reservoir) | ||
| end | ||
|
|
||
| def histogram_with(exemplar_filter:, exemplar_reservoir: nil) | ||
| meter = build_sdk_meter(exemplar_filter: exemplar_filter) | ||
| meter.create_histogram('bench.exemplar.histogram', exemplar_reservoir: exemplar_reservoir) | ||
| end | ||
|
|
||
| noop_res_counter = counter_with(exemplar_filter: Ex::AlwaysOnExemplarFilter, | ||
| exemplar_reservoir: Ex::NoopExemplarReservoir.new) | ||
| simple_res_counter = counter_with(exemplar_filter: Ex::AlwaysOnExemplarFilter, | ||
| exemplar_reservoir: Ex::SimpleFixedSizeExemplarReservoir.new) | ||
|
|
||
| aligned_histogram = histogram_with(exemplar_filter: Ex::AlwaysOnExemplarFilter, | ||
| exemplar_reservoir: Ex::AlignedHistogramBucketExemplarReservoir.new) | ||
| simple_histogram = histogram_with(exemplar_filter: Ex::AlwaysOnExemplarFilter, | ||
| exemplar_reservoir: Ex::SimpleFixedSizeExemplarReservoir.new) | ||
| noop_histogram = histogram_with(exemplar_filter: Ex::AlwaysOnExemplarFilter, | ||
| exemplar_reservoir: Ex::NoopExemplarReservoir.new) | ||
|
robbkidd marked this conversation as resolved.
|
||
|
|
||
| Benchmark.ips do |x| | ||
| x.report('counter Noop reservoir') { noop_res_counter.add(1) } | ||
| x.report('counter SimpleFixedSize reservoir') { simple_res_counter.add(1) } | ||
| x.report('histogram AlignedHistogramBucket') { aligned_histogram.record(42) } | ||
| x.report('histogram SimpleFixedSize reservoir') { simple_histogram.record(42) } | ||
| x.report('histogram Noop reservoir') { noop_histogram.record(42) } | ||
| x.compare! | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| require_relative 'bench_helper' | ||
|
|
||
| sdk_meter = build_sdk_meter | ||
| sdk_counter = sdk_meter.create_counter('bench.sdk.counter') | ||
| sdk_histogram = sdk_meter.create_histogram('bench.sdk.histogram') | ||
| sdk_gauge = sdk_meter.create_gauge('bench.sdk.gauge') | ||
| sdk_updown = sdk_meter.create_up_down_counter('bench.sdk.updown') | ||
|
|
||
| Benchmark.ips do |x| | ||
| x.report('SDK counter#add') { sdk_counter.add(1) } | ||
| x.report('SDK histogram#record') { sdk_histogram.record(1) } | ||
| x.report('SDK gauge#record') { sdk_gauge.record(1) } | ||
| x.report('SDK up_down_counter#add') { sdk_updown.add(1) } | ||
| x.compare! | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| require_relative 'bench_helper' | ||
|
|
||
| noop_meter = OpenTelemetry::Metrics::Meter.new | ||
| noop_counter = noop_meter.create_counter('bench.noop.counter') | ||
| noop_histogram = noop_meter.create_histogram('bench.noop.histogram') | ||
| noop_gauge = noop_meter.create_gauge('bench.noop.gauge') | ||
| noop_updown = noop_meter.create_up_down_counter('bench.noop.updown') | ||
|
|
||
| Benchmark.ips do |x| | ||
| x.report('noop counter#add') { noop_counter.add(1) } | ||
| x.report('noop histogram#record') { noop_histogram.record(1) } | ||
| x.report('noop gauge#record') { noop_gauge.record(1) } | ||
| x.report('noop up_down_counter#add') { noop_updown.add(1) } | ||
| x.compare! | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| require_relative 'bench_helper' | ||
|
|
||
| # No view registered | ||
| no_view_counter = build_sdk_meter.create_counter('bench.view.counter') | ||
|
|
||
| # One matching view | ||
| match_counter = build_sdk_meter( | ||
| views: [['bench.view.counter', { aggregation: Agg::Sum.new }]] | ||
| ).create_counter('bench.view.counter') | ||
|
|
||
| # One non-matching view (different instrument name) | ||
| nomatch_counter = build_sdk_meter( | ||
| views: [['other.counter', { aggregation: Agg::Sum.new }]] | ||
| ).create_counter('bench.view.counter') | ||
|
|
||
| # Three matching views | ||
| multi_provider = OpenTelemetry::SDK::Metrics::MeterProvider.new | ||
| 3.times { multi_provider.add_view('bench.view.counter', aggregation: Agg::Sum.new) } | ||
| multi_provider.add_metric_reader(new_reader) | ||
| multi_counter = multi_provider.meter('bench').create_counter('bench.view.counter') | ||
|
|
||
| Benchmark.ips do |x| | ||
| x.report('counter#add (no view registered)') { no_view_counter.add(1) } | ||
| x.report('counter#add (1 non-matching view)') { nomatch_counter.add(1) } | ||
| x.report('counter#add (1 matching view)') { match_counter.add(1) } | ||
| x.report('counter#add (3 matching views)') { multi_counter.add(1) } | ||
| x.compare! | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec| | |
|
|
||
| spec.add_dependency 'opentelemetry-api', '~> 1.0' | ||
|
|
||
| spec.add_development_dependency 'benchmark-ipsa', '~> 0.2.0' | ||
| spec.add_development_dependency 'benchmark-ips', '~> 2.14.0' | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this not be added to the gemfile instead?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am ok with either put here or in gemfile
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why would it move to the Gemfile?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because currently we are needing to disable the rubocop cop so it doesn't fail, it would increase consistency with contrib & renovate natively updates it etc.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we want to change the way we handle the dev dependencies in this repository, what do you think about moving them all at once in a different PR?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @thompson-tomo, apologies, I just saw #2095 |
||
| spec.add_development_dependency 'minitest', '~> 5.0' | ||
| spec.add_development_dependency 'opentelemetry-test-helpers', '~> 0.3.0' | ||
| spec.add_development_dependency 'rake', '~> 13.3' | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.