Classic-only histogram: consider synchronized block instead of multi-LongAdder for observe() hot path
@zeitlinger đang làm issue này rồi.
Từ ngày 25/2/2026.
Đánh giá
Issue này chưa được đánh giá.
Mô tả
Context
While benchmarking the Prometheus shim PoC (bridging Prometheus client API to the OTel SDK), I found that classic-only histograms are 30% faster through the OTel SDK than through native Prometheus.
Benchmark numbers (JMH, single thread)
| Path | observe() latency |
|---|---|
| Native Prometheus (classic-only) | 10.5 ns |
| OTel SDK (explicit bucket histogram) | 7.3 ns |
Root cause
Native Prometheus doObserve() uses 3 separate CAS-based atomics per call:
classicBuckets[i].add(1)—LongAddersum.add(value)—DoubleAddercount.increment()—LongAdder
Plus a buffer.append() CAS attempt and volatile reads for reset/scale-down state.
The OTel SDK uses a single synchronized block with plain +=/++ arithmetic:
synchronized (lock) {
this.sum += value;
this.count++;
this.counts[bucketIndex]++;
// min/max tracking
}
In uncontended (single-thread) benchmarks, HotSpot elides the uncontended lock and optimizes the plain arithmetic freely, beating the multi-CAS approach.
Suggestion
For classic-only histograms (where nativeInitialSchema == CLASSIC_HISTOGRAM), consider an alternative doObserve() implementation that uses a synchronized block with plain fields instead of multiple LongAdder/DoubleAdder instances. The buffer mechanism (needed for native histogram scale-down) could also be bypassed in classic-only mode.
This wouldn't affect native or hybrid histograms, which still need the current design.
Multi-threaded consideration
The LongAdder approach was chosen for multi-threaded scalability (striped cells reduce contention). A synchronized block would serialize threads. However:
- Most real-world
observe()calls happen on different label-value combinations (different data points), so contention on a single data point is rare - Even under contention, the critical section is very short (~5 ns of arithmetic), so lock hold time is minimal
- A benchmark with 4 threads would clarify the actual tradeoff
Not a high priority — 10.5 ns is already excellent. But worth considering if classic histogram performance matters.
- Ngôn ngữ chính
- Java
- Star
- 2.3k
- Fork
- 833
- Merge trung bình
- 2 ngày 16 giờ
- Pull request đã merge (30 ngày)
- 86
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của prometheus/client_java
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
prometheus/client_java#2416 · 1 bình luận ·
-
Switch Micrometer compatibility workflow to upstream once typed-descriptor path becomes default Đang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 86/100
prometheus/client_java#2182 ·
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
prometheus/client_java#2306 · 9 bình luận · 4 reaction ·
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 32/100
prometheus/client_java#2084 · 3 bình luận ·
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
prometheus/client_java#2075 · 10 bình luận · 1 reaction ·
Tất cả issue của prometheus/client_java
Issue tương tự
-
Bug Java Platform: Java
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
getsentry/sentry-java#6138 · 1 bình luận ·
-
bug needs triage p2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
GoogleCloudPlatform/DataflowTemplates#4273 · 1 bình luận ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
-
bug needs triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100