CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-hdrhistogram--hdr-histogram

High Dynamic Range (HDR) Histogram for recording and analyzing value distributions with configurable precision across wide dynamic ranges.

Pending
Overview
Eval results
Files

concurrent-histograms.mddocs/

Concurrent Histogram Variants

Thread-safe histogram implementations providing different levels of concurrency support for multi-threaded environments. Each variant offers distinct trade-offs between performance, thread safety guarantees, and feature support.

AtomicHistogram

Lock-free, wait-free histogram using atomic operations for count updates. Provides thread-safe recording with limited synchronization for other operations.

public class AtomicHistogram extends Histogram {
    
    // Constructors
    public AtomicHistogram(long highestTrackableValue, int numberOfSignificantValueDigits);
    public AtomicHistogram(long lowestDiscernibleValue, 
                          long highestTrackableValue, 
                          int numberOfSignificantValueDigits);
    public AtomicHistogram(AbstractHistogram source);
    
    // Factory methods
    static AtomicHistogram decodeFromByteBuffer(ByteBuffer buffer, 
                                               long minBarForHighestTrackableValue);
    static AtomicHistogram decodeFromCompressedByteBuffer(ByteBuffer buffer, 
                                                         long minBarForHighestTrackableValue);
    static AtomicHistogram fromString(String base64CompressedHistogramString);
    
    // Implementation methods
    public AtomicHistogram copy();
    public AtomicHistogram copyCorrectedForCoordinatedOmission(long expectedInterval);
}

Thread Safety Characteristics

Thread-Safe Operations:

  • recordValue() - Wait-free atomic recording
  • recordValueWithCount() - Wait-free atomic recording
  • recordValueWithExpectedInterval() - Wait-free atomic recording

NOT Thread-Safe Operations:

  • Auto-resizing operations
  • Value shifting operations
  • Iteration operations
  • Statistical queries (use external synchronization)

Usage Examples

// Create atomic histogram (no auto-resize support)
AtomicHistogram histogram = new AtomicHistogram(1_000_000, 3);

// Thread-safe recording from multiple threads
// Thread 1
new Thread(() -> {
    for (int i = 0; i < 10000; i++) {
        histogram.recordValue(ThreadLocalRandom.current().nextInt(1000));
    }
}).start();

// Thread 2  
new Thread(() -> {
    for (int i = 0; i < 10000; i++) {
        histogram.recordValue(ThreadLocalRandom.current().nextInt(2000));
    }
}).start();

// Reading requires external synchronization
synchronized (histogram) {
    long count = histogram.getTotalCount();
    double mean = histogram.getMean();
    long p95 = histogram.getValueAtPercentile(95.0);
}

Performance Characteristics

  • Recording: Wait-free, no contention between recording threads
  • Memory: Atomic operations may have cache coherency overhead
  • Scalability: Excellent for high-frequency recording scenarios
  • Limitations: Fixed size (no auto-resize), queries need synchronization

ConcurrentHistogram

Fully thread-safe histogram supporting concurrent recording, auto-resizing, and value shifting operations using writer-reader phasing.

public class ConcurrentHistogram extends Histogram {
    
    // Constructors
    public ConcurrentHistogram(int numberOfSignificantValueDigits);
    public ConcurrentHistogram(long highestTrackableValue, int numberOfSignificantValueDigits);
    public ConcurrentHistogram(long lowestDiscernibleValue, 
                              long highestTrackableValue, 
                              int numberOfSignificantValueDigits);
    public ConcurrentHistogram(AbstractHistogram source);
    
    // Factory methods
    static ConcurrentHistogram decodeFromByteBuffer(ByteBuffer buffer, 
                                                   long minBarForHighestTrackableValue);
    static ConcurrentHistogram decodeFromCompressedByteBuffer(ByteBuffer buffer, 
                                                             long minBarForHighestTrackableValue);
    static ConcurrentHistogram fromString(String base64CompressedHistogramString);
    
    // Implementation methods
    public ConcurrentHistogram copy();
    public ConcurrentHistogram copyCorrectedForCoordinatedOmission(long expectedInterval);
}

Auto-Resize Configuration

ConcurrentHistogram automatically enables auto-resize functionality and always sets auto-resize to true, regardless of the constructor used. This ensures that the histogram can dynamically expand to accommodate values outside its initial range without requiring manual configuration.

// All ConcurrentHistogram instances have auto-resize enabled by default
ConcurrentHistogram histogram = new ConcurrentHistogram(3);
// Internal call: histogram.setAutoResize(true) - always executed

// Even if you try to disable it, it remains enabled
histogram.setAutoResize(false);  // This call has no effect
assert histogram.isAutoResize() == true;  // Always true for ConcurrentHistogram

Thread Safety Characteristics

Wait-Free/Lock-Free Operations:

  • recordValue() - Wait-free recording
  • recordValueWithCount() - Wait-free recording
  • recordValueWithExpectedInterval() - Wait-free recording
  • Auto-resizing during recording
  • Value shifting operations

Requires External Synchronization:

  • Queries and statistical operations
  • Iteration operations
  • Histogram manipulation (add(), subtract(), etc.)

Usage Examples

// Create concurrent histogram with auto-resize support
ConcurrentHistogram histogram = new ConcurrentHistogram(3);

// Multiple threads can record concurrently without synchronization
ExecutorService executor = Executors.newFixedThreadPool(10);

for (int t = 0; t < 10; t++) {
    executor.submit(() -> {
        Random random = new Random();
        for (int i = 0; i < 100000; i++) {
            // No synchronization needed for recording
            histogram.recordValue(random.nextInt(10000));
        }
    });
}

// Auto-resizing works concurrently
histogram.recordValue(Long.MAX_VALUE / 4);  // Will auto-resize if needed

// Queries still need external synchronization
synchronized (histogram) {
    histogram.outputPercentileDistribution(System.out, 1.0);
}

executor.shutdown();

Writer-Reader Phasing

ConcurrentHistogram uses a writer-reader phaser for coordination:

// Example of coordinated reading
WriterReaderPhaser phaser = histogram.getWriterReaderPhaser();
phaser.readerLock();
try {
    // Safe to iterate or perform complex queries
    for (HistogramIterationValue value : histogram.recordedValues()) {
        System.out.println("Value: " + value.getValueIteratedTo() + 
                          ", Count: " + value.getCountAtValueIteratedTo());
    }
} finally {
    phaser.readerUnlock();
}

SynchronizedHistogram

Fully thread-safe histogram with synchronized access for all operations. All methods are atomic with respect to modifications.

public class SynchronizedHistogram extends Histogram {
    
    // Constructors
    public SynchronizedHistogram(int numberOfSignificantValueDigits);
    public SynchronizedHistogram(long highestTrackableValue, int numberOfSignificantValueDigits);
    public SynchronizedHistogram(long lowestDiscernibleValue, 
                                long highestTrackableValue, 
                                int numberOfSignificantValueDigits);
    public SynchronizedHistogram(AbstractHistogram source);
    
    // Factory methods
    static SynchronizedHistogram decodeFromByteBuffer(ByteBuffer buffer, 
                                                     long minBarForHighestTrackableValue);
    static SynchronizedHistogram decodeFromCompressedByteBuffer(ByteBuffer buffer, 
                                                               long minBarForHighestTrackableValue);
    
    // Implementation methods  
    public SynchronizedHistogram copy();
    public SynchronizedHistogram copyCorrectedForCoordinatedOmission(long expectedInterval);
}

Thread Safety Characteristics

Fully Synchronized Operations:

  • All operations are synchronized on the histogram instance
  • Recording operations may block other operations
  • Queries are consistent with concurrent modifications
  • All histogram manipulation operations are thread-safe

Usage Examples

// Create synchronized histogram
SynchronizedHistogram histogram = new SynchronizedHistogram(3);

// All operations are thread-safe but may block
Runnable recorder = () -> {
    for (int i = 0; i < 10000; i++) {
        histogram.recordValue(i);  // Thread-safe but blocking
    }
};

Runnable analyzer = () -> {
    while (true) {
        // Thread-safe queries - no external synchronization needed
        long count = histogram.getTotalCount();
        double mean = histogram.getMean();
        long p99 = histogram.getValueAtPercentile(99.0);
        
        System.out.printf("Count: %d, Mean: %.2f, P99: %d%n", count, mean, p99);
        
        try { Thread.sleep(1000); } catch (InterruptedException e) { break; }
    }
};

// Start concurrent operations
new Thread(recorder).start();
new Thread(recorder).start();
new Thread(analyzer).start();

// Complex operations are also thread-safe
SynchronizedHistogram other = new SynchronizedHistogram(3);
other.recordValue(1000);
histogram.add(other);  // Thread-safe histogram addition

Concurrency Comparison

FeatureAtomicHistogramConcurrentHistogramSynchronizedHistogram
Recording PerformanceExcellent (wait-free)Excellent (wait-free)Good (blocking)
Auto-resize SupportNoYesYes
Value ShiftingNoYesYes
Query Thread SafetyExternal sync neededExternal sync neededBuilt-in
Memory OverheadLowMedium (phaser)Low
Use CaseHigh-frequency recordingFull concurrent featuresSimple thread safety

Choosing the Right Variant

Use AtomicHistogram When:

  • High-frequency recording from multiple threads
  • Fixed value range is acceptable
  • Reading/analysis happens infrequently with controlled synchronization
  • Maximum recording performance is critical
// High-frequency latency recording
AtomicHistogram latencyHist = new AtomicHistogram(10_000_000, 3);  // Max 10s latency

// Multiple producer threads
producers.forEach(producer -> 
    producer.onLatencyMeasured(latency -> latencyHist.recordValue(latency))
);

// Periodic analysis with synchronization
scheduledExecutor.scheduleAtFixedRate(() -> {
    synchronized (latencyHist) {
        analyzeLatencyDistribution(latencyHist);
    }
}, 0, 10, TimeUnit.SECONDS);

Use ConcurrentHistogram When:

  • Need full concurrent feature support
  • Auto-resizing is required
  • Value shifting operations are needed
  • Can coordinate reading/analysis phases
// Auto-resizing concurrent histogram for varying workloads
ConcurrentHistogram responseTime = new ConcurrentHistogram(3);

// Multiple threads recording different ranges
recordingThreads.forEach(thread -> 
    thread.onResponse(time -> responseTime.recordValue(time))
);

// Coordinated analysis
WriterReaderPhaser phaser = responseTime.getWriterReaderPhaser();
phaser.readerLock();
try {
    generateReport(responseTime);
} finally {
    phaser.readerUnlock();
}

Use SynchronizedHistogram When:

  • Simple thread safety is preferred over performance
  • Frequent queries from multiple threads
  • Blocking behavior is acceptable
  • Easy integration with existing synchronized code
// Simple thread-safe histogram for mixed read/write workloads
SynchronizedHistogram requestSize = new SynchronizedHistogram(3);

// Recording thread
requestHandler.onRequest(req -> requestSize.recordValue(req.getSize()));

// Monitoring threads can query safely
monitoringThreads.forEach(monitor ->
    monitor.scheduleReporting(() -> {
        // No synchronization needed
        double avgSize = requestSize.getMean();
        long p95Size = requestSize.getValueAtPercentile(95.0);
        reportMetrics(avgSize, p95Size);
    })
);

Performance Considerations

AtomicHistogram Performance

  • Best for recording-heavy workloads
  • Cache line contention possible with many threads
  • Consider using multiple histograms and merging periodically

ConcurrentHistogram Performance

  • Writer-reader phaser adds coordination overhead
  • Excellent scaling for mixed read/write workloads
  • Auto-resize may cause temporary pauses

SynchronizedHistogram Performance

  • Simplest implementation but may limit scalability
  • Good for lower-frequency operations
  • Contention increases with thread count

Memory Models and Visibility

All concurrent variants provide proper memory visibility guarantees:

  • Recorded values are visible across threads
  • Statistical queries reflect all recorded values up to the query point
  • No additional memory barriers needed for basic usage

Install with Tessl CLI

npx tessl i tessl/maven-org-hdrhistogram--hdr-histogram

docs

concurrent-histograms.md

core-operations.md

double-histograms.md

index.md

iterators.md

recorders.md

serialization.md

specialized-variants.md

utilities.md

tile.json