HdrHistogram.NET

repository·main·Indexed 41 days ago

https://github.com/hdrhistogram/hdrhistogram.net

The official .NET port of the Java HdrHistogram library, designed for efficiently capturing and reporting high dynamic range response time measurements with low latency and a small memory footprint. It provides multiple implementations including LongHistogram, IntHistogram, ShortHistogram, and SynchronizedHistogram to balance performance, memory usage, and thread safety.

Tokens
12.5K
Snippets
37
Records
59
Agent score
51%

What's inside HdrHistogram.NET

  1. Choose the appropriate Histogram implementation

    main

    HdrHistogram provides several implementations of HistogramBase depending on your data type and concurrency needs:

    • LongHistogram: The standard implementation. It tracks value counts in long fields and is recommended for most use cases.
    • IntHistogram: Tracks counts in int fields. Use this when you have a limited range of counts and want to save memory (e.g., when managing tens of thousands of histograms in memory).
    • ShortHistogram: Tracks counts in short fields. Use this for very small count ranges to minimize storage.
    • SynchronizedHistogram: A thread-safe variant.

    Note on Concurrency: LongHistogram, IntHistogram, and ShortHistogram are NOT internally synchronized and do not use atomic variables to maintain high performance. For multi-threaded updates, you should either use SynchronizedHistogram or use a pattern of per-thread non-synchronized histograms that are aggregated later.

  2. Understand HdrHistogram architecture and class hierarchy

    main
    To understand how the library is structured, refer to the Architecture documentation. Key concepts include the class hierarchy (e.g., HistogramBase, LongHistogram), the Recorder pattern for high-performance recording, thread safety considerations, and the memory model used to maintain high precision with low overhead.
  3. How HdrHistogram works and its core concepts

    main

    HdrHistogram (High Dynamic Range Histogram) is designed for low-latency, high-precision recording of integer value distributions.

    Key Characteristics

    • Fixed memory footprint: Memory is allocated at construction and is never allocated during the recording phase, ensuring no GC pressure or latency spikes.
    • O(1) recording: Recording a value takes constant time regardless of how much data has already been collected.
    • Configurable precision: You can specify 1-5 significant digits of precision.
    • Configurable range: You define the lowestTrackableValue and highestTrackableValue.
    • Efficient queries: Provides fast percentile and distribution queries.

    Value Recording Model

    Values are placed into "buckets" using a logarithmic bucketing scheme. This ensures constant relative precision across the entire configured range. The buckets are determined by your chosen significant digits and the value range.

  4. Use V2 Log Encoding for timestamped histograms

    main

    The V2 Log format is a text-based format for multiple histograms. Each line represents a record in the format: [Tag=]LogTimeStamp,IntervalLengthSeconds,MaxTime,CompressedPayload

    Key Components:

    • Metadata: Lines starting with # (e.g., #[StartTime: X.XXX]) provide context like start time or base time.
    • Tags: An optional field at the start of a line (Tag=value,). Values must not contain commas, spaces, or newlines.
    • Payload: The histogram is stored as a Base64-encoded string (starting with the prefix HIST).

    Example Log Line: Tag=api,2.000,1.000,55.0,HISTFAAAAxxxxxx...

  5. Configure Histogram dynamic range and precision

    main

    A Histogram's behavior is defined by two primary configuration parameters:

    1. highestTrackableValue: Controls the dynamic range. This is the maximum value the histogram can record.
    2. numberOfSignificantValueDigits: Controls the resolution (precision). This determines the number of significant digits maintained across the entire range.

    For example, a histogram with a highestTrackableValue of 3,600,000,000 and numberOfSignificantValueDigits of 3 will maintain a resolution of approximately 0.1% (3 significant digits) across the whole range. This ensures that even at the maximum value, the quantization error is controlled.

  6. Understand Histogram binary encoding and persistence

    main

    HdrHistogram uses a specific binary encoding format (V2) for persistence and interoperability. This includes:

    • V2 Log Format: The specification for the log format.
    • LEB128 Encoding: Used for count encoding within the format.
    • Compression: Support for DEFLATE and other compression techniques to reduce the footprint of encoded histograms.
  7. Choose the right Histogram implementation

    main

    HdrHistogram.NET provides several concrete implementations of HistogramBase. Choose one based on your memory constraints, the maximum count you expect per bucket, and your thread-safety requirements.

    Non-Thread-Safe Implementations

    Use these for single-threaded recording. They are safe for read-only access from multiple threads after recording is complete.

    ClassWord SizeCount TypeMax Count per Bucket
    LongHistogram8 byteslong[]~9 quintillion
    IntHistogram4 bytesint[]~2 billion
    ShortHistogram2 bytesshort[]~65,000

    Thread-Safe Implementations

    Use these if multiple threads must record values simultaneously.

    ClassWord SizeCount TypeThread-Safe
    LongConcurrentHistogram8 bytesAtomicLongArrayYes
    IntConcurrentHistogram4 bytesAtomicIntArrayYes
  8. Follow the Test Naming Convention

    main

    Test names should be descriptive and follow these patterns to explain the scenario clearly:

    • {Method}_returns_{expected}_when_{condition}
    • {Method}_throws_{exception}_if_{condition}
    • {Property}_is_{value}_after_{action}

    Examples:

    • RecordValue_increments_TotalCount
    • Constructor_throws_ArgumentException_if_precision_exceeds_5
    • GetValueAtPercentile_returns_correct_value_for_median
  9. Use the Recorder pattern for concurrent recording and periodic reading

    main

    The Recorder class provides thread-safe interval recording using a double-buffering mechanism. This is ideal for high-throughput scenarios where you want to record data continuously while a separate thread periodically captures snapshots.

    1. Recording: Multiple threads can call recorder.RecordValue(latency) concurrently.
    2. Snapshotting: A single reader thread can call recorder.GetIntervalHistogram(). This returns a stable copy (snapshot) of the data collected in the previous interval, while the Recorder immediately swaps to a new active histogram to continue recording without interruption.

    This pattern uses a WriterReaderPhaser to ensure atomic swaps without locking during the recording process.

    // Multiple threads can call:
    recorder.RecordValue(latency);
    
    // Periodic reader (single thread):
    var snapshot = recorder.GetIntervalHistogram();
    // snapshot is a stable copy, recording continues uninterrupted
  10. Use the Binary Encoded Histogram Format

    main

    The binary format is structured as follows:

    • Cookie (4 bytes): Identifies the version (V2 uses 478450452 or 0x1c849314).
    • Payload Length (4 bytes): The length of the compressed data.
    • Compressed Payload: The DEFLATE-compressed histogram data.

    Technical Details:

    • Compression: Uses DEFLATE (Zlib RFC-1950).
    • Count Encoding: Uses Little Endian Base128 (LEB128) with ZigZag encoding. Consecutive zeros are compressed using Zero Run-Length Encoding (where a negative integer represents the number of consecutive zeros).
    • Byte Order: Header fields are Big-endian; LEB128 counts are Little-endian.
  11. Handle equivalent values in a Histogram

    main

    Because of finite resolution, multiple adjacent integer values may be considered "equivalent" (they fall into the same bucket). HdrHistogram provides methods to manage this:

    • Determine the lowest and highest equivalent values for a given value.
    • Check if two values are equivalent.
    • Find the next non-equivalent value for a given value (useful for loops to avoid double-counting).