Skip to content

Bound instruments#8527

Open
jack-berg wants to merge 5 commits into
open-telemetry:mainfrom
jack-berg:bound-instruments
Open

Bound instruments#8527
jack-berg wants to merge 5 commits into
open-telemetry:mainfrom
jack-berg:bound-instruments

Conversation

@jack-berg

Copy link
Copy Markdown
Member

Supersedes #8314.

Java implementation of open-telemetry/opentelemetry-specification#5050

Adds bound instrument support to opentelemetry-api-incubator. Usage:

// Initialize instrument
ExtendedLongCounter rolls =
    (ExtendedLongCounter)
        meter
            .counterBuilder("dice.rolls")
            .setDescription("The number of times each side of the die was rolled")
            .setUnit("{roll}")
            .build();

// Bind known attribute sets
BoundLongCounter face1 = rolls.bind(Attributes.of(AttributeKey.longKey("roll.value"), 1L));
BoundLongCounter face2 = rolls.bind(Attributes.of(AttributeKey.longKey("roll.value"), 2L));
// ... omitted for brevity

// Record
face1.add(1);
face1.add(1);
face2.add(1);

In the original prototype #8314, I went with an API where bind returns the existing instrument interface. I.e. you call LongCounter bind(Attributes) from LongCounter. The user is meant to call add(long), but the other methods bind / add(long, Attributes), add(long, Attributes, Context) are still available. The user could call bind again without issue, but calling the add overloads which accept Attributes remove the performance gain.

Instead, in this PR I've added dedicated Bound{Type}{Instrument} interfaces, e.g:

public interface BoundLongCounter {
  void add(long value);
  void add(long value, Context context);
}

These are impossible to misuse, and allows for supporting a value + Context overload. Additionally, they lend themselves to extending later with a close() method. Something that cannot be reasonably done if we reuse the existing instrument interfaces because whereas calling close on a bound instrument should only close that series, calling close on the instrument should either close all the series or have some sort of selector for the series that should be closed. The semantics are too different to reuse.

In the original prototype #8314 I sketched out what it would look like in its final form with APIs added directly to the stable opentelemetry-api. Here I've added it to the incubator such that its actually in a position to merge and evaluate.

Performance characteristics

Main vs. PR - unbound instruments

No material change in performance. All changes are within variance.

Details
Threads Temporality Cardinality Instrument Baseline (ops/s) After (ops/s) Δ ops/s Δ %
1 DELTA 1 COUNTER_SUM 115,152,051 114,617,697 -534,354 -0.5%
1 DELTA 1 UP_DOWN_COUNTER_SUM 107,098,645 98,304,556 -8,794,089 -8.2%
1 DELTA 1 GAUGE_LAST_VALUE 35,005,255 37,674,212 +2,668,957 +7.6%
1 DELTA 1 HISTOGRAM_EXPLICIT 62,460,147 63,228,739 +768,592 +1.2%
1 DELTA 1 HISTOGRAM_BASE2_EXPONENTIAL 44,408,116 44,375,427 -32,689 -0.1%
1 DELTA 128 COUNTER_SUM 96,117,356 98,378,163 +2,260,807 +2.4%
1 DELTA 128 UP_DOWN_COUNTER_SUM 98,476,180 97,477,391 -998,789 -1.0%
1 DELTA 128 GAUGE_LAST_VALUE 31,483,812 30,110,931 -1,372,881 -4.4%
1 DELTA 128 HISTOGRAM_EXPLICIT 68,599,287 72,058,400 +3,459,113 +5.0%
1 DELTA 128 HISTOGRAM_BASE2_EXPONENTIAL 41,433,509 41,916,945 +483,436 +1.2%
1 CUMULATIVE 1 COUNTER_SUM 157,495,783 162,432,610 +4,936,827 +3.1%
1 CUMULATIVE 1 UP_DOWN_COUNTER_SUM 160,294,388 162,249,755 +1,955,367 +1.2%
1 CUMULATIVE 1 GAUGE_LAST_VALUE 93,318,198 58,535,417 -34,782,781 -37.3%
1 CUMULATIVE 1 HISTOGRAM_EXPLICIT 97,228,372 101,428,756 +4,200,384 +4.3%
1 CUMULATIVE 1 HISTOGRAM_BASE2_EXPONENTIAL 40,593,886 44,012,751 +3,418,865 +8.4%
1 CUMULATIVE 128 COUNTER_SUM 110,161,456 104,449,781 -5,711,675 -5.2%
1 CUMULATIVE 128 UP_DOWN_COUNTER_SUM 110,068,669 104,886,600 -5,182,069 -4.7%
1 CUMULATIVE 128 GAUGE_LAST_VALUE 112,738,056 107,481,737 -5,256,319 -4.7%
1 CUMULATIVE 128 HISTOGRAM_EXPLICIT 74,952,762 77,916,887 +2,964,125 +4.0%
1 CUMULATIVE 128 HISTOGRAM_BASE2_EXPONENTIAL 42,269,050 43,712,347 +1,443,297 +3.4%
4 DELTA 1 COUNTER_SUM 16,258,535 15,315,611 -942,924 -5.8%
4 DELTA 1 UP_DOWN_COUNTER_SUM 16,747,722 17,777,515 +1,029,793 +6.1%
4 DELTA 1 GAUGE_LAST_VALUE 17,791,993 18,128,924 +336,931 +1.9%
4 DELTA 1 HISTOGRAM_EXPLICIT 12,726,304 12,100,735 -625,569 -4.9%
4 DELTA 1 HISTOGRAM_BASE2_EXPONENTIAL 9,617,985 10,936,415 +1,318,430 +13.7%
4 DELTA 128 COUNTER_SUM 74,789,707 73,330,786 -1,458,921 -2.0%
4 DELTA 128 UP_DOWN_COUNTER_SUM 70,993,325 68,982,332 -2,010,993 -2.8%
4 DELTA 128 GAUGE_LAST_VALUE 52,314,233 50,100,548 -2,213,685 -4.2%
4 DELTA 128 HISTOGRAM_EXPLICIT 60,543,218 59,402,821 -1,140,397 -1.9%
4 DELTA 128 HISTOGRAM_BASE2_EXPONENTIAL 63,307,519 64,875,458 +1,567,939 +2.5%
4 CUMULATIVE 1 COUNTER_SUM 76,605,686 71,709,669 -4,896,017 -6.4%
4 CUMULATIVE 1 UP_DOWN_COUNTER_SUM 77,770,455 76,583,695 -1,186,760 -1.5%
4 CUMULATIVE 1 GAUGE_LAST_VALUE 28,278,168 30,429,962 +2,151,794 +7.6%
4 CUMULATIVE 1 HISTOGRAM_EXPLICIT 18,177,733 17,403,539 -774,194 -4.3%
4 CUMULATIVE 1 HISTOGRAM_BASE2_EXPONENTIAL 13,631,238 16,273,417 +2,642,179 +19.4%
4 CUMULATIVE 128 COUNTER_SUM 99,134,253 99,349,480 +215,227 +0.2%
4 CUMULATIVE 128 UP_DOWN_COUNTER_SUM 102,460,174 99,328,833 -3,131,341 -3.1%
4 CUMULATIVE 128 GAUGE_LAST_VALUE 107,587,080 110,666,315 +3,079,235 +2.9%
4 CUMULATIVE 128 HISTOGRAM_EXPLICIT 81,669,108 80,661,595 -1,007,513 -1.2%
4 CUMULATIVE 128 HISTOGRAM_BASE2_EXPONENTIAL 75,765,539 74,788,388 -977,151 -1.3%

Bound vs. unbound instruments

Big improvement in uncontended cases. Mixed results with contention.

Bound instruments remove a map lookup. This is great on paper, but in practice, the CMH is meticulously engineered such that it's not the main point of contention. When the map lookup is removed, the other points of contention (i.e. AtomicInteger used to coordinate between record / collect threads for delta, and LongAdder used to actually accumulate values) get put under more pressure, which reduces performance.

To improve, we need to look to improving the concurrent throughput of the new hotspots. Probably possible, but not easy. That work can be done in a followup because:

  1. This change doesn't cause any degredation of the existing stable APIs.
  2. There is some real improvement for the uncontended cases.
Details
Threads Temporality Cardinality Instrument Baseline (ops/s) After (ops/s) Δ ops/s Δ %
1 DELTA 1 COUNTER_SUM 114,617,697 194,872,623 +80,254,926 +70.0%
1 DELTA 1 UP_DOWN_COUNTER_SUM 98,304,556 172,777,285 +74,472,729 +75.8%
1 DELTA 1 GAUGE_LAST_VALUE 37,674,212 49,722,838 +12,048,626 +32.0%
1 DELTA 1 HISTOGRAM_EXPLICIT 63,228,739 119,486,633 +56,257,894 +89.0%
1 DELTA 1 HISTOGRAM_BASE2_EXPONENTIAL 44,375,427 51,534,583 +7,159,156 +16.1%
1 DELTA 128 COUNTER_SUM 98,378,163 157,920,163 +59,542,000 +60.5%
1 DELTA 128 UP_DOWN_COUNTER_SUM 97,477,391 162,443,498 +64,966,107 +66.6%
1 DELTA 128 GAUGE_LAST_VALUE 30,110,931 33,996,041 +3,885,110 +12.9%
1 DELTA 128 HISTOGRAM_EXPLICIT 72,058,400 88,787,407 +16,729,007 +23.2%
1 DELTA 128 HISTOGRAM_BASE2_EXPONENTIAL 41,916,945 48,030,359 +6,113,414 +14.6%
1 CUMULATIVE 1 COUNTER_SUM 162,432,610 216,362,809 +53,930,199 +33.2%
1 CUMULATIVE 1 UP_DOWN_COUNTER_SUM 162,249,755 218,106,233 +55,856,478 +34.4%
1 CUMULATIVE 1 GAUGE_LAST_VALUE 58,535,417 97,508,070 +38,972,653 +66.6%
1 CUMULATIVE 1 HISTOGRAM_EXPLICIT 101,428,756 135,137,011 +33,708,255 +33.2%
1 CUMULATIVE 1 HISTOGRAM_BASE2_EXPONENTIAL 44,012,751 52,068,573 +8,055,822 +18.3%
1 CUMULATIVE 128 COUNTER_SUM 104,449,781 210,057,265 +105,607,484 +101.1%
1 CUMULATIVE 128 UP_DOWN_COUNTER_SUM 104,886,600 216,448,581 +111,561,981 +106.4%
1 CUMULATIVE 128 GAUGE_LAST_VALUE 107,481,737 216,824,669 +109,342,932 +101.7%
1 CUMULATIVE 128 HISTOGRAM_EXPLICIT 77,916,887 110,792,775 +32,875,888 +42.2%
1 CUMULATIVE 128 HISTOGRAM_BASE2_EXPONENTIAL 43,712,347 51,295,882 +7,583,535 +17.3%
4 DELTA 1 COUNTER_SUM 15,315,611 18,505,255 +3,189,644 +20.8%
4 DELTA 1 UP_DOWN_COUNTER_SUM 17,777,515 18,233,235 +455,720 +2.6%
4 DELTA 1 GAUGE_LAST_VALUE 18,128,924 17,252,923 -876,001 -4.8%
4 DELTA 1 HISTOGRAM_EXPLICIT 12,100,735 13,611,643 +1,510,908 +12.5%
4 DELTA 1 HISTOGRAM_BASE2_EXPONENTIAL 10,936,415 11,088,929 +152,514 +1.4%
4 DELTA 128 COUNTER_SUM 73,330,786 46,028,140 -27,302,646 -37.2%
4 DELTA 128 UP_DOWN_COUNTER_SUM 68,982,332 51,257,547 -17,724,785 -25.7%
4 DELTA 128 GAUGE_LAST_VALUE 50,100,548 40,972,298 -9,128,250 -18.2%
4 DELTA 128 HISTOGRAM_EXPLICIT 59,402,821 56,608,878 -2,793,943 -4.7%
4 DELTA 128 HISTOGRAM_BASE2_EXPONENTIAL 64,875,458 52,702,151 -12,173,307 -18.8%
4 CUMULATIVE 1 COUNTER_SUM 71,709,669 54,860,415 -16,849,254 -23.5%
4 CUMULATIVE 1 UP_DOWN_COUNTER_SUM 76,583,695 46,719,515 -29,864,180 -39.0%
4 CUMULATIVE 1 GAUGE_LAST_VALUE 30,429,962 29,564,480 -865,482 -2.8%
4 CUMULATIVE 1 HISTOGRAM_EXPLICIT 17,403,539 19,028,474 +1,624,935 +9.3%
4 CUMULATIVE 1 HISTOGRAM_BASE2_EXPONENTIAL 16,273,417 20,283,605 +4,010,188 +24.6%
4 CUMULATIVE 128 COUNTER_SUM 99,349,480 102,097,180 +2,747,700 +2.8%
4 CUMULATIVE 128 UP_DOWN_COUNTER_SUM 99,328,833 95,765,882 -3,562,951 -3.6%
4 CUMULATIVE 128 GAUGE_LAST_VALUE 110,666,315 67,936,132 -42,730,183 -38.6%
4 CUMULATIVE 128 HISTOGRAM_EXPLICIT 80,661,595 82,813,433 +2,151,838 +2.7%
4 CUMULATIVE 128 HISTOGRAM_BASE2_EXPONENTIAL 74,788,388 79,753,095 +4,964,707 +6.6%

@jack-berg jack-berg requested a review from a team as a code owner June 23, 2026 20:11
@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.48276% with 74 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.46%. Comparing base (8d01d31) to head (4da6ca0).
⚠️ Report is 14 commits behind head on main.

Files with missing lines Patch % Lines
...ry/api/incubator/metrics/ExtendedDefaultMeter.java 40.00% 24 Missing ⚠️
.../internal/state/DeltaSynchronousMetricStorage.java 81.31% 8 Missing and 9 partials ⚠️
...in/java/io/opentelemetry/sdk/metrics/SdkMeter.java 0.00% 16 Missing ⚠️
...sdk/metrics/internal/state/EmptyMetricStorage.java 0.00% 5 Missing ⚠️
...rnal/state/CumulativeSynchronousMetricStorage.java 71.42% 2 Missing and 2 partials ⚠️
...elemetry/sdk/metrics/ExtendedSdkDoubleCounter.java 84.61% 1 Missing and 1 partial ⚠️
...emetry/sdk/metrics/ExtendedSdkDoubleHistogram.java 84.61% 1 Missing and 1 partial ⚠️
...ntelemetry/sdk/metrics/ExtendedSdkLongCounter.java 83.33% 1 Missing and 1 partial ⚠️
...elemetry/sdk/metrics/ExtendedSdkLongHistogram.java 84.61% 1 Missing and 1 partial ⚠️

❌ Your patch check has failed because the patch coverage (74.48%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #8527      +/-   ##
============================================
- Coverage     78.51%   78.46%   -0.06%     
- Complexity     8601     8666      +65     
============================================
  Files          1013     1013              
  Lines         29148    29416     +268     
  Branches       3631     3655      +24     
============================================
+ Hits          22887    23081     +194     
- Misses         5420     5478      +58     
- Partials        841      857      +16     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

1 participant