Criterium Clojure Benchmarking Library

repository·develop·Indexed 22 days ago

https://github.com/hugoduncan/criterium

A Clojure benchmarking library for accurate computation time measurements on the JVM. It features warm-up periods, statistical processing, GC management, and allocation tracking via a native JVM agent. The library includes tools for statistical validation against GNU R and provides high-level functions like `bench` and `quick-bench` for performance analysis.

Tokens
11.1K
Snippets
30
Records
59
Agent score
79%

What's inside Criterium

  1. Understand the design decisions for streaming statistics

    develop

    Criterium's streaming statistics implementation is designed to estimate and represent statistical distributions from streaming data samples with minimal storage. The design prioritizes handling uni-modal or bi-modal distributions (approximately log-normal) and supports both quantile queries and density estimation.

    While several approaches were evaluated (including Reservoir Sampling, P-square, and Streaming Moments), the project's design direction favors T-digest for its balance of efficiency and accuracy.

    Key characteristics of the chosen approach (T-digest):

    • High storage efficiency for simple modal patterns.
    • Fast quantile queries.
    • Robustness against gradual changes in distribution shape.
    • Low sensitivity to parameter tuning.
  2. Understand the agent extraction error contract

    develop

    When extraction fails, Criterium uses structured errors to identify exactly where the process broke down. Errors are categorized by a :stage key.

    Common extraction stages include:

    • :lock-dir-create
    • :open-lock
    • :acquire-lock
    • :create-temp
    • :copy
    • :verify-hash (triggered if SHA256 integrity check fails)
    • :atomic-move
    • :set-executable
    • :verify-permissions
    • :resolve-binary

    If an unsupported platform is detected, the system will log a clear error to stderr including the system's os.name and os.arch via platform/describe.

  3. How agent extraction and persistence works

    develop

    Criterium uses a content-addressed extraction mechanism for its native agent. When an agent is extracted, it is placed in a temporary directory (typically java.io.tmpdir) with a filename derived from its SHA256 hash.

    Persistence Model:

    • Default Behavior: Extraction is persistent. The extracted binary is NOT deleted when the producing JVM exits. This ensures that the path returned by (jvm-opts) remains valid for subsequent JVM processes.
    • Ephemeral Behavior (Opt-in): If you require the agent to be cleaned up automatically, you must explicitly pass the :cleanup-on-exit? true option to the extraction function.
    • Deduplication: Because files are named by their SHA256 hash, multiple processes or different versions of the JAR will reuse the same file if the binary is identical, preventing unbounded growth in the temp directory.
  4. How Criterium handles unsupported platforms

    develop

    Criterium is designed with graceful degradation. If the agent is unavailable (due to an unsupported platform or a missing binary), the library continues to function with reduced capabilities:

    • Timing measurements: Basic timing continues to work normally.
    • Allocation tracking: Returns empty results.
    • API behavior: (agent/loaded?) returns false and (agent/jvm-opts) returns [].
    • Logging: A warning is logged on the first attempt to use agent features.

    This ensures that your benchmarking code remains portable across all platforms, even if allocation data is unavailable.

  5. Histogram Core Requirements and Data Formats

    develop

    Criterium's histogram implementation follows specific requirements for input, binning logic, and output structure.

    Input Requirements

    • Required: A vector of numeric values.
    • Optional: A pre-computed Interquartile Range (IQR) value.
    • Validation: The input vector must be non-empty.

    Bin Determination Logic

    Binning is performed using the Freedman-Diaconis rule to determine bin width: width = 2 * IQR * n^(-1/3)

    If an IQR is not provided, it is calculated automatically. The number of bins is derived from the data range (max - min) and the computed bin width. Bin edges are designed to exactly cover the data range to ensure all data points are included.

    Output Format

    The histogram returns a map containing the following fields:

    • bin counts: A vector of counts per bin.
    • bin centers: A vector of the center values for each bin.
    • bin width: The constant width used for all bins.
    • probability density values: A vector of density values.
    • total number of samples: The count of input samples (for verification).
    • min value: The minimum value in the input (for verification).
    • max value: The maximum value in the input (for verification).
  6. Configure Bench Plans for specialized analysis

    develop

    Bench plans define how Criterium analyzes and presents data.

    • default: The standard plan. Includes JIT warmup, bootstrap confidence intervals, outlier detection, and KDE density estimation.
    • histogram: Uses criterium.bench-plans/histogram. Provides non-parametric distribution analysis with Knuth optimal binning, KDE density estimation, and mode detection.
    • distribution-analysis: Uses criterium.bench-plans/distribution-analysis. Performs parametric distribution fitting (gamma, log-normal, Weibull), shape statistics (skewness, kurtosis), goodness-of-fit tests, and Q-Q plots.
    (require '[criterium.bench-plans :as plans])
    
    ;; Histogram analysis
    (bench/bench (my-function) :bench-plan plans/histogram)
    
    ;; Distribution analysis
    (bench/bench (my-function) :bench-plan plans/distribution-analysis)
  7. Criterium versioning and changelog format

    develop

    Criterium uses semantic versioning for tags and conventional commits for automatic changelog generation via git-cliff.

    Version Format

    Tags follow the pattern vMAJOR.MINOR.PATCH (e.g., v0.5.0, v0.5.1).

    Changelog Commit Types

    The changelog is automatically categorized based on commit prefixes:

    • feat: - Features
    • fix: - Bug Fixes
    • docs: - Documentation
    • perf: - Performance
    • refactor: - Refactor
    • test: - Testing
    • Other commits are categorized as Miscellaneous.
  8. Manage measurement overhead estimation

    develop

    Criterium estimates the time taken by its own measurement overhead and stores it in the criterium.core/estimated-overhead-cache var.

    If you notice small negative times for quick functions (often due to high system load during estimation), you can force a recalculation by calling criterium.core/estimated-overhead!. For consistency across different JVM processes, you may choose to set this value to a constant manually.

  9. Understand Criterium benchmarking terminology

    develop

    To use Criterium effectively, you should understand its core concepts:

    Execution & Measurement

    • Benchmark: A complete performance measurement process including warmup, sample collection, and statistical analysis.
    • Warmup: An initial execution period designed to allow the JVM to optimize code (via JIT compilation) before measurements are taken.
    • Sample: A single timing measurement of an expression's execution.
    • Batch Size: The number of times an expression is evaluated within a single timing measurement.
    • Evaluation Count: The total number of times an expression is evaluated throughout the entire benchmarking process.
    • Elapsed Time: The total wall clock time taken to execute an expression.

    Data & Analysis

    • Measured: A wrapper used to capture both timing information and the actual results of the expression being benchmarked.
    • Metrics: The various measurements collected, such as elapsed time, memory usage, or GC activity.
    • State: The captured context or arguments required to execute a benchmarked expression.
    • Bootstrap: A statistical resampling technique used to estimate confidence intervals and improve the accuracy of performance metrics.
    • Transform: Functions used to convert raw sample values into transformed representations.

    Infrastructure & Tooling

    • Collect Plan: A strategy defining how samples are collected, including the warmup period and measurement intervals.
    • Pipeline: The sequence of operations used for collecting and processing benchmark measurements.
    • Viewer: A component that presents benchmark results in specific formats (e.g., printing to console or a portal).
    • Allocation Tracking: The process of monitoring and recording memory allocations during execution using JVM tooling.
  10. Configure release prerequisites for Criterium

    develop

    To ensure the automated release workflow functions correctly, the following prerequisites must be met:

    1. GitHub Secrets: The Release environment in the GitHub repository settings must contain the following secrets:
      • CLOJARS_USERNAME
      • CLOJARS_PASSWORD
    2. Version Configuration: The version number must be correctly set in build/src/build/version.clj before initiating the release.
  11. Quick Start with Criterium

    develop

    To perform a basic benchmark in Criterium 0.5.x, require criterium.bench and use the bench macro. This macro handles JVM warmup and provides statistical analysis automatically.

    Note: The 0.4.x API criterium.core/bench is deprecated. Always use criterium.bench/bench for new code.

    (require '[criterium.bench :as bench])
    
    (bench/bench (+ 1 1))