From e039050e484c1e8872dc121d78c586c6076ebcb3 Mon Sep 17 00:00:00 2001 From: CodSpeed Bot Date: Wed, 24 Jun 2026 03:18:07 +0000 Subject: [PATCH] Add CodSpeed performance benchmarks and CI workflow --- .github/workflows/codspeed.yml | 37 ++++++++++++ Cargo.toml | 5 ++ README.md | 1 + benches/kee_benchmarks.rs | 102 +++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+) create mode 100644 .github/workflows/codspeed.yml create mode 100644 benches/kee_benchmarks.rs diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml new file mode 100644 index 0000000..22e3ffb --- /dev/null +++ b/.github/workflows/codspeed.yml @@ -0,0 +1,37 @@ +name: CodSpeed + +on: + push: + branches: + - main + pull_request: + # `workflow_dispatch` allows CodSpeed to trigger backtest + # performance analysis in order to generate initial data. + workflow_dispatch: + +permissions: + contents: read + id-token: write # for OpenID Connect authentication with CodSpeed + +jobs: + codspeed: + name: Run benchmarks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Setup rust toolchain, cache and cargo-codspeed binary + uses: moonrepo/setup-rust@v0 + with: + channel: stable + cache-target: release + bins: cargo-codspeed + + - name: Build the benchmark target(s) + run: cargo codspeed build + + - name: Run the benchmarks + uses: CodSpeedHQ/action@v4 + with: + mode: simulation + run: cargo codspeed run diff --git a/Cargo.toml b/Cargo.toml index b3a5ecd..83c1e15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,10 @@ path = "src/main.rs" name = "kee" path = "src/lib.rs" +[[bench]] +name = "kee_benchmarks" +harness = false + [dependencies] clap = { version = "4.4", features = ["derive"] } clap_complete = "4.4" @@ -43,6 +47,7 @@ mockall = "0.12" serial_test = "3.0" assert_cmd = "2.0" predicates = "3.1" +criterion = { version = "5.0.0", package = "codspeed-criterion-compat" } [features] default = [] diff --git a/README.md b/README.md index 059143a..301b943 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Tests](https://github.com/aichholzer/kee/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/aichholzer/kee/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/aichholzer/kee/graph/badge.svg?token=YpgPwMbazO)](https://codecov.io/gh/aichholzer/kee) [![Latest version](https://img.shields.io/crates/v/kee.svg)](https://crates.io/crates/kee) +[![CodSpeed](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://app.codspeed.io/aichholzer/kee?utm_source=badge) ![License](https://img.shields.io/crates/l/kee.svg)
![macOS](https://img.shields.io/badge/-macOS-black) ![Linux](https://img.shields.io/badge/-Linux-green) diff --git a/benches/kee_benchmarks.rs b/benches/kee_benchmarks.rs new file mode 100644 index 0000000..da9663a --- /dev/null +++ b/benches/kee_benchmarks.rs @@ -0,0 +1,102 @@ +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; +use kee::{KeeConfig, ProfileInfo}; + +/// Build a representative ProfileInfo with a unique suffix. +fn make_profile(i: usize) -> ProfileInfo { + ProfileInfo { + profile_name: format!("kee-profile-{i}"), + sso_start_url: format!("https://acme-{i}.awsapps.com/start"), + sso_region: "ap-southeast-2".to_string(), + sso_account_id: format!("{:012}", 100000000000u64 + i as u64), + sso_role_name: "AdministratorAccess".to_string(), + session_name: format!("acme-session-{i}"), + production: i % 3 == 0, + } +} + +/// Build a KeeConfig populated with `n` profiles. +fn make_config(n: usize) -> KeeConfig { + let mut config = KeeConfig::new(); + for i in 0..n { + config.add_profile(format!("profile-{i}"), make_profile(i)); + } + if n > 0 { + config.set_current_profile(Some(format!("profile-{}", n / 2))); + } + config +} + +fn bench_profile_serialization(c: &mut Criterion) { + let profile = make_profile(42); + c.bench_function("profile_info_serialize", |b| { + b.iter(|| serde_json::to_string(black_box(&profile)).unwrap()) + }); + + let json = serde_json::to_string(&profile).unwrap(); + c.bench_function("profile_info_deserialize", |b| { + b.iter(|| { + let p: ProfileInfo = serde_json::from_str(black_box(&json)).unwrap(); + p + }) + }); +} + +fn bench_config_build(c: &mut Criterion) { + let mut group = c.benchmark_group("config_build"); + for size in [10usize, 100, 1000] { + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| { + b.iter(|| make_config(black_box(size))) + }); + } + group.finish(); +} + +fn bench_config_lookup(c: &mut Criterion) { + let config = make_config(1000); + c.bench_function("config_get_profile", |b| { + b.iter(|| black_box(config.get_profile(black_box("profile-750")))) + }); + + c.bench_function("config_list_profiles", |b| { + b.iter(|| black_box(config.list_profiles())) + }); +} + +fn bench_config_serialization(c: &mut Criterion) { + let mut group = c.benchmark_group("config_serialization"); + for size in [10usize, 100, 1000] { + let config = make_config(size); + group.bench_with_input(BenchmarkId::new("serialize", size), &config, |b, config| { + b.iter(|| serde_json::to_string(black_box(config)).unwrap()) + }); + + let json = serde_json::to_string(&config).unwrap(); + group.bench_with_input(BenchmarkId::new("deserialize", size), &json, |b, json| { + b.iter(|| { + let c: KeeConfig = serde_json::from_str(black_box(json)).unwrap(); + c + }) + }); + } + group.finish(); +} + +fn bench_config_remove(c: &mut Criterion) { + c.bench_function("config_remove_profile", |b| { + b.iter_batched( + || make_config(100), + |mut config| black_box(config.remove_profile(black_box("profile-50"))), + criterion::BatchSize::SmallInput, + ) + }); +} + +criterion_group!( + benches, + bench_profile_serialization, + bench_config_build, + bench_config_lookup, + bench_config_serialization, + bench_config_remove +); +criterion_main!(benches);