Skip to main content

Crate all_the_time

Crate all_the_time 

Source
Expand description

Processor time tracking utilities for benchmarks and performance analysis.

This package provides utilities to track processor time during code execution, enabling analysis of processor usage patterns in benchmarks and performance tests.

The core functionality includes:

  • Session - Configures processor time tracking and provides access to tracking data
  • Report - Thread-safe processor time statistics that can be merged and processed independently
  • Operation - Measures per-iteration processor time for a repeated operation
  • ThreadSpan / ProcessSpan - Active measurements that record when dropped

This package is not meant for use in production, serving only as a development tool.

§Benchmarking

The typical pattern is to drive measurement from Criterion’s iter_custom() function, feeding its chosen iteration count into iterations() so each recorded span covers a whole sample rather than a single iteration:

use std::hint::black_box;
use std::time::Instant;

use all_the_time::Session;
use criterion::Criterion;

fn bench(c: &mut Criterion) {
    let session = Session::new();

    let operation = session.operation("my_operation");
    c.bench_function("my_operation", |b| {
        b.iter_custom(|iters| {
            let start = Instant::now();
            let _span = operation.measure_thread().iterations(iters);

            for _ in 0..iters {
                black_box(42_u64.wrapping_mul(2));
            }

            start.elapsed()
        });
    });

    // When `session` is dropped, the recorded statistics are printed to
    // stdout and written to the Cargo target directory as JSON.
}

You must call iterations() on the span before it is dropped. Failure to do so will result in a panic.

§Human-readable summary

When a Session is dropped it prints a table of per-iteration figures to stdout, one row per operation:

Processor time statistics:

| Operation    | Per iteration |
|--------------|---------------|
| decode_value |          84ns |
| encode_value |         120ns |

§Machine-readable output

Dropping a Session also writes JSON files (one per operation) into the Cargo target directory at target/all_the_time/<operation>.json, with operation names sanitized to be filesystem-safe.

Both outputs are produced automatically, so a typical benchmark only needs to create a session and record work.

§Measuring a variable amount of work

You do not need to specify the iteration count up front, as long as it is provided before the span is dropped.

This allows you to measure work whose extent is not known at the start.

use std::hint::black_box;

use all_the_time::Session;

fn main() {
    let session = Session::new();
    let operation = session.operation("drain_queue");

    let span = operation.measure_thread();

    let mut processed = 0_u64;

    while let Some(item) = get_next_item() {
        black_box(item.refresh());
        processed += 1;
    }

    drop(span.iterations(processed));
}

§Thread vs process measurement

You can choose between tracking processor time spent by the current thread or by the entire process. The latter is useful for measuring multithreaded workloads.

use all_the_time::Session;

let session = Session::new();

// Track thread processor time
{
    let op = session.operation("thread_work");
    let _span = op.measure_thread().iterations(1);
    do_some_work();
}

// Track process processor time (all threads)
{
    let op = session.operation("process_work");
    let _span = op.measure_process().iterations(1);
    do_some_multithreaded_work();
}

§Overhead

Capturing a single measurement by calling measure_xyz() incurs an overhead of approximately 500 nanoseconds on an arbitrary sample machine.

It is crucial that you measure multiple iterations in the same sample to amortize this overhead. This is the purpose of the iter_custom() pattern described above.

Operating without batching, by measuring individual iterations, is only viable for macrobenchmarks for which a single iteration is a large unit of work (e.g. an HTTP request).

§Session management

Multiple Session instances can be used concurrently as they track processor time independently. Each session maintains its own set of operations and statistics.

While Session itself is single-threaded, reports from sessions can be converted to thread-safe Report instances and sent to other threads for processing:

use std::thread;
use std::time::Duration;

use all_the_time::Session;

let session = Session::new();
let operation = session.operation("work");
let _span = operation.measure_thread().iterations(1);
// Some work happens here

let report = session.to_report();

// Reports are `Send`, so they can be moved to another thread for processing.
let total_time = thread::spawn(move || {
    report
        .operations()
        .map(|(_, op)| op.total_processor_time())
        .sum::<Duration>()
})
.join()
.unwrap();

println!("Total processor time: {total_time:?}");

Structs§

Operation
Aggregates data from repeated measurements of a single operation.
OperationStatistics
Per-iteration processor-time statistics for one operation.
ProcessSpan
A measurement of process-wide processor time over the span’s lifetime.
Report
Thread-safe processor time tracking report.
ReportOperation
Processor time statistics for a single operation in a report.
Session
Manages processor time tracking session state and contains operations.
ThreadSpan
A measurement of the current thread’s processor time over the span’s lifetime.