Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions metrics_api/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ eval_gemfile '../contrib/Gemfile.shared'

group :test, :development do
gem 'opentelemetry-api', path: '../api', require: false
gem 'opentelemetry-metrics-sdk', path: '../metrics_sdk', require: false
gem 'pry'
gem 'pry-byebug' unless RUBY_ENGINE == 'jruby'
end
29 changes: 29 additions & 0 deletions metrics_api/benchmarks/README.md
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
Comment thread
robbkidd marked this conversation as resolved.
```

## 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) |
Comment thread
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`) |
29 changes: 29 additions & 0 deletions metrics_api/benchmarks/aggregation_bench.rb
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
34 changes: 34 additions & 0 deletions metrics_api/benchmarks/attributes_bench.rb
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
29 changes: 29 additions & 0 deletions metrics_api/benchmarks/bench_helper.rb
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
Comment thread
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
28 changes: 28 additions & 0 deletions metrics_api/benchmarks/exemplar_filter_bench.rb
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
Comment thread
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
38 changes: 38 additions & 0 deletions metrics_api/benchmarks/exemplar_reservoir_bench.rb
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)
Comment thread
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
21 changes: 21 additions & 0 deletions metrics_api/benchmarks/instrument_bench.rb
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
21 changes: 21 additions & 0 deletions metrics_api/benchmarks/noop_instrument_bench.rb
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
34 changes: 34 additions & 0 deletions metrics_api/benchmarks/view_bench.rb
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
2 changes: 1 addition & 1 deletion metrics_api/opentelemetry-metrics-api.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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'
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.

Should this not be added to the gemfile instead?

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.

I am ok with either put here or in gemfile

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why would it move to the Gemfile?

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.

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.

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.

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?

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.

@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'
Expand Down
Loading