divan

repository·main·Indexed 23 days ago

https://github.com/nvzqz/divan

A statistically-comfy benchmarking library for Rust projects (version 0.1.21). It provides the #[divan::bench] attribute for registering functions, a Bencher API for contextual benchmarking, and detailed statistical output including time and allocation metrics. Features include support for generic benchmarks, benchmark grouping via #[bench_group], and a CLI for filtering and managing benchmark execution.

Tokens
6.6K
Snippets
21
Records
39
Agent score
80%

What's inside divan

  1. Install and set up Divan

    main

    To use Divan in your Rust project, follow these steps:

    1. Ensure you are using Rust 1.80.0 or later.
    2. Add divan to your [dev-dependencies] in Cargo.toml.
    3. Define a benchmark target in Cargo.toml by setting harness = false for your benchmark name.

    Example Cargo.toml configuration:

    [dev-dependencies]
    divan = "0.1.21"
    
    [[bench]]
    name = "example"
    harness = false
  2. Run Divan internal benchmarks locally

    main

    To benchmark the internals of the Divan crate itself, clone the repository and use cargo bench targeting the internal_benches package. Use the -q flag to suppress non-essential output.

    git clone https://github.com/nvzqz/divan.git
    cd divan
    
    cargo bench -q -p internal_benches
  3. Create and run a Divan benchmark

    main
    1. Create a benchmark file (e.g., benches/example.rs) within your crate directory.
    2. Implement a main function that calls divan::main() to run the registered benchmarks.
    3. Use the #[divan::bench] attribute to register functions for benchmarking. You can pass arguments to the benchmark function using the args parameter.
    4. Run the benchmarks using cargo bench.

    Example benchmark file:

    fn main() {
        // Run registered benchmarks.
        divan::main();
    }
    
    // Register a `fibonacci` function and benchmark it over multiple cases.
    #[divan::bench(args = [1, 2, 4, 8, 16, 32])]
    fn fibonacci(n: u64) -> u64 {
        if n <= 1 {
            1
        } else {
            fibonacci(n - 2) + fibonacci(n - 1)
        }
    }
  4. Run Divan practical example benchmarks locally

    main

    To run the practical example benchmarks provided in the repository, clone the repository and use cargo bench targeting the examples package with all features enabled. This is useful for seeing how Divan is applied to real-world scenarios.

    git clone https://github.com/nvzqz/divan.git
    cd divan
    
    cargo bench -q -p examples --all-features
  5. Run benchmarks in test mode to check for panics

    main

    If you want to verify that your benchmarked functions run without panicking without actually measuring their performance, you can run the benchmark runner in --test mode. This is useful for CI environments.

    You can use either of the following commands:

    cargo bench -- --test

    or

    cargo test --benches
  6. Understand Divan's Benchmarking Modes

    main

    Divan operates in different modes depending on the execution context and configuration. The initial_mode logic determines how the benchmark will behave:

    1. Test Mode: Triggered if the shared context action is a test. This is typically used for quick verification.
    2. Collect Mode: Triggered if a sample_size is explicitly provided in the options. Divan will collect a specific number of samples.
    3. Tune Mode: The default mode when no sample size is specified. It uses a sample_size of 1 to tune the number of iterations needed for stable measurements.
  7. Prevent compiler optimizations with `black_box`

    main

    Use black_box to prevent the compiler from optimizing away code based on known inputs or outputs.

    Best Practices:

    • Inputs: Use it on inputs (like indices or data) to prevent the compiler from unrolling loops or removing bounds checks.
    • Outputs: Use it on outputs to ensure the compiler doesn't remove the code because the result is unused.
    • Avoid: Do not use black_box inside the implementation of the code you are benchmarking, as this will overly pessimize the measurement.

    For cases where you want to explicitly signal that an output is being discarded, use black_box_drop.

    use divan::black_box;
    
    const INDEX: usize = // ...
    # 0;
    const SLICE: &[u8] = // ...
    # &[];
    
    #[divan::bench]
    fn bench() {
        # fn work<T>(_: T) {}
        work(&SLICE[black_box(INDEX)..]);
    }
  8. How to handle ignored benchmarks

    main

    You can control whether benchmarks marked with #[ignore] are executed using the following behaviors:

    • Default (No): Only runs benchmarks that are NOT marked as ignored.
    • --include-ignored: Runs all benchmarks, including those marked as ignored.
    • --ignored: Runs ONLY benchmarks that are marked as ignored.
  9. Use custom counters to measure throughput

    main

    Divan allows you to track metrics like bytes, characters, cycles, or items processed in each iteration of a benchmark. This helps in measuring throughput (e.g., MB/s or items/s).

    You can provide a counter in three ways:

    1. Using the #[divan::bench(counters = ...)] or #[divan::bench_group(counters = ...)] macros.
    2. Using Bencher::counter within the benchmark function.
    3. Using Bencher::input_counter.

    Common counter types include:

    • BytesCount: Measures data in bytes.
    • CharsCount: Measures data in Unicode code points (char).
    • CyclesCount: Measures cycles (displayed as Hertz).
    • ItemsCount: Measures the number of items processed.
    use divan::counter::BytesCount;
    
    #[divan::bench]
    fn slice_into_vec(bencher: divan::Bencher) {
        let ints: &[i32] = &[1, 2, 3];
    
        let bytes = BytesCount::of_slice(ints);
    
        bencher
            .counter(bytes)
            .bench(|| -> Vec<i32> {
                divan::black_box(ints).into()
            });
    }
  10. Automatically count inputs with `count_inputs_as::<C>()`

    main

    If your input type implements AsCountUInt (e.g., it's a u64, usize, or a slice), you can use count_inputs_as::<C>() to automatically set up an input_counter for a specific counter type C (like ItemsCount, BytesCount, etc.).

    use divan::{Bencher, counter::ItemsCount};
    
    #[divan::bench]
    fn range_to_vec(bencher: Bencher) {
        bencher
            .with_inputs(|| -> usize {
                // ...
                # 0
            })
            .count_inputs_as::<ItemsCount>()
            .bench_values(|n| -> Vec<usize> {
                (0..n).collect()
            });
    }
  11. Run generic benchmarks with `#[bench]`

    main

    The #[bench] attribute supports generating multiple benchmark instances for a single function by varying type parameters or constant values. This is useful for testing how an algorithm performs across different data types or sizes.

    • Varying Types: Use the types option to provide a list of types to monomorphize the function with.
    • Varying Constants: Use the consts option to provide a list of constant values to use as generic parameters.