Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Performance Measurement

FutureSDR performance work usually happens at two levels:

  • use Mocker to benchmark one block implementation,
  • use the perf/ applications to benchmark complete flowgraphs and scheduler or buffer configurations.

Always measure release builds. Debug builds are useful while developing, but they do not represent runtime performance.

Block Microbenchmarks

For a single block, use Mocker. It runs the block directly, without a scheduler and without a full Flowgraph, so the benchmark mostly measures the block’s work() implementation and the buffer operations it performs.

This is the right tool for comparing implementation choices inside one block, checking how performance changes with input size, or writing a Criterion benchmark around a small processing kernel. The repository’s apply benchmark is a compact example:

cargo bench --bench apply

Mocker is not a replacement for full-flowgraph benchmarks. It intentionally removes scheduling, message routing between blocks, and end-to-end stream topology effects.

Parameter Sweeps

The perf/ directory contains standalone benchmark applications for measuring complete configurations. These examples are useful when the question is about scheduler choice, buffer behavior, number of stages, number of pipes, sample counts, or other flowgraph-level parameters.

Many of the directories contain a Makefile that iterates over a parameter grid, writes CSV files to perf-data/, and provides helper targets for selected configurations:

cd perf/null
make

Inspect the local Makefile before running a sweep. Some benchmarks run for a long time, and the parameter ranges are intentionally broad.

Profiling One Configuration

To understand where time is spent in one specific configuration, profile that configuration directly. Samply works well for this because it records a profile and opens an interactive view in the browser:

samply record -- cargo run --release

For an independent example workspace, run it from that directory or pass the manifest path:

samply record -- cargo run --release --manifest-path=perf/null/Cargo.toml -- --config=flow

Enable debug symbols for release builds so the profile contains useful function names and source locations:

[profile.release]
debug = true

Add this to the Cargo.toml of the workspace you are profiling. For the root crate, that is the repository root. For a benchmark under perf/, it is usually the Cargo.toml inside that benchmark directory.

The flame graph view is often the most useful starting point. Look for unexpectedly large functions, allocation-heavy paths, synchronization overhead, and time spent outside the block code when the goal is to tune scheduling or buffering.

Stable Measurements

For reproducible results, reduce system noise. One practical approach on a systemd-based Linux machine is to move normal system work onto a small CPU set and run the benchmark on the remaining CPUs.

First, restrict the normal system slices to the CPUs reserved for the operating system:

SYSTEM_CPUS=0,1

sudo systemctl set-property --runtime -- user.slice AllowedCPUs=${SYSTEM_CPUS}
sudo systemctl set-property --runtime -- system.slice AllowedCPUs=${SYSTEM_CPUS}
sudo systemctl set-property --runtime -- init.scope AllowedCPUs=${SYSTEM_CPUS}

Then start the benchmark in its own transient unit on the CPUs reserved for the measurement:

sudo systemd-run --uid=$(id -u) --slice=sdr --wait -P -p AllowedCPUs=2,3 -d -- cargo run --release

The perf/migrate-processes.sh and perf/revert-processes.sh scripts show the same pattern for the repository benchmarks. The settings are runtime-only, but reset them after a measurement run or reboot before using the machine normally.