Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 8 additions & 84 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ jobs:
benchmarks:
name: Run Benchmarks
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
timeout-minutes: 15

steps:
- name: Checkout repository
Expand All @@ -32,9 +30,6 @@ jobs:
with:
components: clippy, rustfmt

- name: Install cargo-hack
run: cargo install cargo-hack

- name: Cache cargo registry
uses: actions/cache@v4
with:
Expand All @@ -46,19 +41,18 @@ jobs:
${{ runner.os }}-cargo-

- name: Build benchmarks
run: cargo build --benches --all-features
run: cargo build --benches --release

- name: Run benchmarks
run: cargo bench --all-features 2>&1 | tee bench_output.txt
continue-on-error: true
- name: Run benchmarks (CI mode)
run: cargo bench --bench write_bench --bench read_bench --bench scan_bench --bench mixed_bench --bench stress_bench -- --noplot
env:
CI: true

- name: Upload benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmark-results
path: |
bench_output.txt
target/criterion
path: target/criterion/benchmark-results.json
retention-days: 30

- name: Generate benchmark summary
Expand Down Expand Up @@ -120,77 +114,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Run clippy on benchmarks
run: cargo clippy --all-targets --all-features -- -D warnings
run: cargo clippy --all-targets -- -D warnings

- name: Format check
run: cargo fmt --all -- --check

benchmark-comparison:
name: Compare with Baseline
runs-on: ubuntu-latest
needs: benchmarks
if: github.event_name == 'pull_request'
steps:
- name: Download baseline results
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: baseline-results
path: baseline

- name: Download current results
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: benchmark-results
path: current

- name: Compare benchmarks
run: |
if [ -f baseline/bench_output.txt ] && [ -f current/bench_output.txt ]; then
echo "Comparing benchmarks..."
# TODO: Implement actual comparison logic with cargo-benchcmp or similar
else
echo "No baseline available yet β€” skipping comparison. This is expected on the first run."
fi

- name: Upload comparison report
if: always()
uses: actions/upload-artifact@v4
with:
name: comparison-report
path: comparison-report.md
if-no-files-found: ignore

performance-regression:
name: Check Performance Regressions
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Set up benchmarks
run: |
cargo install cargo-benchcmp

- name: Restore baseline
uses: actions/cache@v4
with:
path: .baseline
key: ${{ runner.os }}-benchmark-baseline

- name: Run baseline benchmarks
run: cargo bench --all-features -- --output-format benchstat
continue-on-error: true
env:
CI: true

- name: Check for regressions > 10%
run: |
echo "Checking for performance regressions..."
# TODO: Implement regression detection
# This would typically compare against stored baselines
63 changes: 50 additions & 13 deletions benches/mixed_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ use rand::Rng;
use std::path::PathBuf;
use tempfile::TempDir;

fn configure_criterion() -> Criterion {
let mut c = Criterion::default();
if std::env::var("CI").is_ok() {
c = c
.sample_size(10)
.warm_up_time(std::time::Duration::from_secs(1))
.measurement_time(std::time::Duration::from_secs(3));
}
c
}

fn setup_temp_dir(name: &str) -> (TempDir, PathBuf) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let path = temp_dir.path().join(name);
Expand Down Expand Up @@ -32,7 +43,12 @@ fn generate_value(index: usize, value_size: usize) -> Vec<u8> {

/// Benchmark YCSB Type A: 50% read, 50% write (uniform)
fn bench_ycsb_type_a(c: &mut Criterion) {
for num_keys in [10_000usize, 100_000] {
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![10_000]
} else {
vec![10_000, 100_000]
};
for num_keys in num_keys_arr {
let mut group = c.benchmark_group("ycsb_type_a");
group.throughput(Throughput::Elements(1000));

Expand Down Expand Up @@ -83,7 +99,12 @@ fn bench_ycsb_type_a(c: &mut Criterion) {

/// Benchmark YCSB Type B: 95% read, 5% write (read-heavy)
fn bench_ycsb_type_b(c: &mut Criterion) {
for num_keys in [10_000usize, 100_000] {
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![10_000]
} else {
vec![10_000, 100_000]
};
for num_keys in num_keys_arr {
let mut group = c.benchmark_group("ycsb_type_b");
group.throughput(Throughput::Elements(1000));

Expand Down Expand Up @@ -138,7 +159,12 @@ fn bench_ycsb_type_b(c: &mut Criterion) {

/// Benchmark YCSB Type C: 100% read (read-only)
fn bench_ycsb_type_c(c: &mut Criterion) {
for num_keys in [10_000usize, 100_000, 1_000_000] {
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![10_000, 100_000]
} else {
vec![10_000, 100_000, 1_000_000]
};
for num_keys in num_keys_arr {
let mut group = c.benchmark_group("ycsb_type_c");
group.throughput(Throughput::Elements(1000));

Expand Down Expand Up @@ -192,7 +218,12 @@ fn bench_ycsb_type_c(c: &mut Criterion) {

/// Benchmark balanced workload: 50% read, 50% write
fn bench_workload_balanced(c: &mut Criterion) {
for num_keys in [10_000usize, 100_000] {
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![10_000]
} else {
vec![10_000, 100_000]
};
for num_keys in num_keys_arr {
let mut group = c.benchmark_group("workload_balanced");
group.throughput(Throughput::Elements(1000));

Expand Down Expand Up @@ -246,7 +277,12 @@ fn bench_workload_balanced(c: &mut Criterion) {

/// Benchmark read-heavy workload
fn bench_workload_read_heavy(c: &mut Criterion) {
for num_keys in [10_000usize, 100_000] {
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![10_000]
} else {
vec![10_000, 100_000]
};
for num_keys in num_keys_arr {
let mut group = c.benchmark_group("workload_read_heavy");
group.throughput(Throughput::Elements(1000));

Expand Down Expand Up @@ -301,7 +337,12 @@ fn bench_workload_read_heavy(c: &mut Criterion) {

/// Benchmark write-heavy workload
fn bench_workload_write_heavy(c: &mut Criterion) {
for num_keys in [10_000usize, 100_000] {
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![10_000]
} else {
vec![10_000, 100_000]
};
for num_keys in num_keys_arr {
let mut group = c.benchmark_group("workload_write_heavy");
group.throughput(Throughput::Bytes(100));

Expand Down Expand Up @@ -352,13 +393,9 @@ fn bench_workload_write_heavy(c: &mut Criterion) {
}

criterion_group!(
mixed_benches,
bench_ycsb_type_a,
bench_ycsb_type_b,
bench_ycsb_type_c,
bench_workload_balanced,
bench_workload_read_heavy,
bench_workload_write_heavy,
name = mixed_benches;
config = configure_criterion();
targets = bench_ycsb_type_a, bench_ycsb_type_b, bench_ycsb_type_c, bench_workload_balanced, bench_workload_read_heavy, bench_workload_write_heavy
);

criterion_main!(mixed_benches);
62 changes: 46 additions & 16 deletions benches/read_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Through
use std::path::PathBuf;
use tempfile::TempDir;

fn configure_criterion() -> Criterion {
let mut c = Criterion::default();
if std::env::var("CI").is_ok() {
c = c
.sample_size(10)
.warm_up_time(std::time::Duration::from_secs(1))
.measurement_time(std::time::Duration::from_secs(3));
}
c
}

/// Setup a temporary directory for benchmark testing
fn setup_temp_dir(name: &str) -> (TempDir, PathBuf) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
Expand Down Expand Up @@ -35,7 +46,11 @@ fn generate_value(index: usize, value_size: usize) -> Vec<u8> {

/// Benchmark read operations with all keys in MemTable
fn bench_read_memtable(c: &mut Criterion) {
let num_keys_arr = [1_000usize, 10_000, 100_000, 1_000_000];
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![1_000, 10_000]
} else {
vec![1_000, 10_000, 100_000, 1_000_000]
};
for &num_keys in &num_keys_arr {
let mut group = c.benchmark_group("read_memtable");
group.throughput(Throughput::Elements(num_keys as u64));
Expand Down Expand Up @@ -83,7 +98,12 @@ fn bench_read_memtable(c: &mut Criterion) {

/// Benchmark read operations with all keys in SSTable (cold cache)
fn bench_read_sstable_cold(c: &mut Criterion) {
for num_keys in [1_000usize, 10_000, 100_000] {
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![1_000, 10_000]
} else {
vec![1_000, 10_000, 100_000]
};
for num_keys in num_keys_arr {
let mut group = c.benchmark_group("read_sstable_cold");
group.throughput(Throughput::Elements(num_keys as u64));

Expand All @@ -96,7 +116,7 @@ fn bench_read_sstable_cold(c: &mut Criterion) {
LsmConfig::builder()
.dir_path(data_dir.clone())
.memtable_max_size(nk * 110 / 2)
.block_cache_size_mb(0)
.block_cache_size_mb(1)
.build()
.unwrap(),
)
Expand Down Expand Up @@ -131,7 +151,12 @@ fn bench_read_sstable_cold(c: &mut Criterion) {

/// Benchmark read operations with cache warmed up
fn bench_read_sstable_warm(c: &mut Criterion) {
for num_keys in [1_000usize, 10_000, 100_000] {
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![1_000, 10_000]
} else {
vec![1_000, 10_000, 100_000]
};
for num_keys in num_keys_arr {
let mut group = c.benchmark_group("read_sstable_warm");
group.throughput(Throughput::Elements(num_keys as u64));

Expand Down Expand Up @@ -184,7 +209,12 @@ fn bench_read_sstable_warm(c: &mut Criterion) {

/// Benchmark Bloom filter effectiveness
fn bench_bloom_filter(c: &mut Criterion) {
for num_keys in [10_000usize, 100_000, 1_000_000] {
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![10_000, 100_000]
} else {
vec![10_000, 100_000, 1_000_000]
};
for num_keys in num_keys_arr {
let mut group = c.benchmark_group("bloom_filter");
group.throughput(Throughput::Elements(num_keys as u64));

Expand All @@ -197,7 +227,7 @@ fn bench_bloom_filter(c: &mut Criterion) {
LsmConfig::builder()
.dir_path(data_dir.clone())
.memtable_max_size(nk * 110 / 2)
.block_cache_size_mb(0)
.block_cache_size_mb(1)
.build()
.unwrap(),
)
Expand Down Expand Up @@ -271,7 +301,7 @@ fn bench_read_latency(c: &mut Criterion) {
LsmConfig::builder()
.dir_path(data_dir.clone())
.memtable_max_size(1_000 * 110 / 2)
.block_cache_size_mb(0)
.block_cache_size_mb(1)
.build()
.unwrap(),
)
Expand Down Expand Up @@ -301,7 +331,11 @@ fn bench_read_latency(c: &mut Criterion) {

/// Benchmark sequential scan performance
fn bench_scan_sequential(c: &mut Criterion) {
let num_keys_arr = [1_000usize, 10_000, 100_000];
let num_keys_arr: Vec<usize> = if std::env::var("CI").is_ok() {
vec![1_000, 10_000]
} else {
vec![1_000, 10_000, 100_000]
};
for &num_keys in &num_keys_arr {
let mut group = c.benchmark_group("scan_sequential");
group.throughput(Throughput::Elements(num_keys as u64));
Expand Down Expand Up @@ -330,7 +364,7 @@ fn bench_scan_sequential(c: &mut Criterion) {
engine.flush_memtable().unwrap();

b.iter(|| {
let results = engine.scan().unwrap();
let results = engine.scan_cf("default", None, None, None).unwrap();
assert_eq!(results.len(), nk);
});

Expand All @@ -344,13 +378,9 @@ fn bench_scan_sequential(c: &mut Criterion) {
}

criterion_group!(
read_benches,
bench_read_memtable,
bench_read_sstable_cold,
bench_read_sstable_warm,
bench_bloom_filter,
bench_read_latency,
bench_scan_sequential,
name = read_benches;
config = configure_criterion();
targets = bench_read_memtable, bench_read_sstable_cold, bench_read_sstable_warm, bench_bloom_filter, bench_read_latency, bench_scan_sequential
);

criterion_main!(read_benches);
Loading
Loading