Speed & Performance

Links:

Существует две основные категории программных бенчмарков: микро-бенчмарки и макро-бенчмарки. Микро-бенчмарки работают на уровне, аналогичном модульным тестам. Макро-бенчмарки работают на уровне, аналогичном интеграционным тестам.

В целом, лучше всего тестировать на максимально низком уровне абстракции. В случае бенчмарков это делает их более простыми в поддержке и помогает уменьшить количество шума в измерениях. Однако, как и наличие некоторых сквозных тестов может быть очень полезным для проверки того, что вся система работает как ожидается, так и наличие макро-бенчмарков может быть очень полезным для обеспечения того, чтобы критические пути в вашем программном обеспечении оставались производительными.

std::time для быстрого замера

use std::time::Instant;

fn array_diff<T: PartialEq>(a: Vec<T>, b: Vec<T>) -> Vec<T> {
    a.into_iter().filter(|x| !b.contains(x)).collect()
}

fn array_diff2<T: PartialEq>(mut a: Vec<T>, b: Vec<T>) -> Vec<T> {
    a.retain(|x| !b.contains(x));
    a
}

fn main() {
    let start = Instant::now(); // начало замера 1
    println!("{:?}", array_diff(vec![1, 2, 2], vec![1]));
    let duration_a = start.elapsed(); // конец

    let start = Instant::now();  // начало замера 2
    println!("{:?}", array_diff2(vec![1, 2, 2], vec![1]));
    let duration_b = start.elapsed(); // конец

    println!("Замеры: 1) {:?}; 2) {:?}", duration_a, duration_b);
}

Для более точного замера следует собирать код с cargo run --release ключом.

Criterion

В Cargo.toml надо добавить зависимости:

[dev-dependencies]
criterion = "0.8"

[[bench]]
name = "my_benchmark"
harness = false

Далее в проекте создать папку и файл benches\my_benchmark.rs:

use criterion::{Criterion, criterion_group, criterion_main};

fn array_diff<T: PartialEq>(a: Vec<T>, b: Vec<T>) -> Vec<T> {
    a.into_iter().filter(|x| !b.contains(x)).collect()
} // функция 1 для проверки 

fn array_diff2<T: PartialEq>(mut a: Vec<T>, b: Vec<T>) -> Vec<T> {
    a.retain(|x| !b.contains(x));
    a
} // функция 2 для проверки 

fn benchmark_functions(c: &mut Criterion) {
    let mut group = c.benchmark_group("Function Comparison");

    group.bench_function("array_diff", |b| {
        b.iter(|| {
            std::hint::black_box(array_diff(vec![1, 2, 2], vec![1]));
        }) // black_box блокирует оптимизации компилятора
    });

    group.bench_function("array_diff2", |b| {
        b.iter(|| {
            std::hint::black_box(array_diff2(vec![1, 2, 2], vec![1]));
        })
    });

    group.finish();
}

criterion_group!(benches, benchmark_functions);
criterion_main!(benches);

Для запуска тестов необходима команда cargo bench. Результаты замеров сохраняются и сравниваются с новыми результатами после внесения изменений.

flamegraph - визуализация стека вызовов

flamegraph — это инструмент для создания интерактивных SVG-диаграмм, которые визуализируют стек вызовов и время, затраченное в каждой функции. Он строится на основе данных от perf (xctrace в macOS) и помогает быстро понять, где сосредоточена нагрузка.

Установка:

cargo install flamegraph

Для того, чтобы видеть заголовки запускаемых функций (не их хэши), нужно в файле cargo.toml добавить профиль разработки. При этом профилирование скорости нужно проводить в режиме release со всеми оптимизациями, поэтому следует сделать отдельный профиль под release, но с debug-символами:

[profile.perfmon]
inherits = "release"
debug = true

Использование:

# принудительно указать свой профиль perfmon (по умолчанию --release)
cargo flamegraph --profile perfmon

# данные по выбранной функции
cargo flamegraph --profile perfmon --flamechart-opts "--grep functionName"

# Профилирование unit-тестов
# Разделитель `--` нужен, если `--unit-test` последний флаг.
cargo flamegraph --unit-test -- test::in::package::with::single::crate
cargo flamegraph --unit-test crate_name -- test::in::package::with::multiple:crate

# Профилирование интеграционных тестов
cargo flamegraph --test test_name

# Профилирование примера из папки examples в workspace
cargo flamegraph --example some_example --features some_features