prometheus / prometheus/client_java

Classic-only histogram: consider synchronized block instead of multi-LongAdder for observe() hot path

Abierto
#1,915 0 comentarios 0 reacciones 1 asignado Ver en GitHub

@zeitlinger ya está trabajando en esto.

Desde el 25/2/2026.

Lenguaje dominante
Java
Estrellas
2.3k
Forks
833
Merge medio
2 d 16 h
PR fusionados (30 d)
86

Descripción

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:

  1. classicBuckets[i].add(1)LongAdder
  2. sum.add(value)DoubleAdder
  3. count.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.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.