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
79 changes: 79 additions & 0 deletions .github/workflows/apex_bench.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Apex Bench

on:
pull_request:
branches: [ "main" ]
paths:
- "rust/apex_sim/**"
- "rust/timsseek/**"
- ".github/workflows/apex_bench.yml"
workflow_dispatch:

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1

# Needed to upsert the sticky results comment on the PR.
permissions:
contents: read
pull-requests: write

jobs:
bench:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Apply crate patches (rustyms gnome.dat stub)
run: |
cargo install --git https://github.com/jspaezp/cargo-patch-crate patch-crate --locked
cargo patch-crate

- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
cache: true

- name: Run apex_sim bench
id: bench
run: |
cargo run -p apex_sim --release --no-default-features --example bench -- 1000 2 > bench_out.txt
echo "----- bench output -----"
cat bench_out.txt

- name: Post results to PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const output = fs.readFileSync('bench_out.txt', 'utf8');
const marker = '<!-- apex-bench -->';
const body = [
marker,
'## Apex-finder bench',
'`cargo run -p apex_sim --release --example bench -- 1000 2`',
'',
'<details><summary>Sensitivity + timing across canonical scenarios</summary>',
'',
'```',
output.trim(),
'```',
'',
'</details>',
'',
`_commit ${context.sha.slice(0, 8)}_`,
].join('\n');

const { owner, repo } = context.repo;
const issue_number = context.issue.number;
const comments = await github.paginate(
github.rest.issues.listComments,
{ owner, repo, issue_number },
);
const existing = comments.find(c => c.body && c.body.includes(marker));
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body });
}
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"rust/timsquery",
"rust/timsquery_cli",
"rust/timsquery_viewer",
"rust/apex_sim",
"rust/speclib_build_cli",
"rust/tims_stage",
"python/timsquery_pyo3"
Expand All @@ -26,6 +27,7 @@ default-members = [
"rust/timsquery",
"rust/timsquery_cli",
"rust/timsquery_viewer",
"rust/apex_sim",
"rust/speclib_build_cli",
"rust/tims_stage"
]
Expand Down
33 changes: 33 additions & 0 deletions rust/apex_sim/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "apex_sim"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true

[[bin]]
name = "apex_sim"
path = "src/main.rs"
# The interactive app needs the GUI stack; skipped under --no-default-features.
required-features = ["gui"]

[features]
default = ["gui"]
# GUI feature: pulls the egui/eframe stack. Disable (--no-default-features) to
# build just the simulation + scoring + bench, e.g. in CI, without egui/winit.
gui = ["dep:egui", "dep:eframe", "dep:egui_plot"]

[dependencies]
timsseek = { path = "../timsseek" }
timsquery = { path = "../timsquery" }

# GUI (optional, gated behind the `gui` feature)
egui = { version = "0.33", features = ["rayon"], optional = true }
eframe = { version = "0.33", default-features = true, features = [
"default_fonts",
], optional = true }
egui_plot = { version = "0.34", optional = true }

# Deterministic synthetic-data RNG
rand = "0.9"
rand_chacha = "0.9"
47 changes: 47 additions & 0 deletions rust/apex_sim/examples/bench.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! Apex-finder bench: runs the whole catalog of canonical scenarios
//! (`apex_sim::bench::canonical_suite`) at once and prints per-scenario detail
//! plus a comparison summary table.
//!
//! Each scenario runs `n_runs` seeds of one fixed config; "sensitivity" is the
//! fraction of runs whose pass-2 apex lands within `tol` cycles of truth.
//!
//! Run:
//! cargo run -p apex_sim --release --example bench
//! cargo run -p apex_sim --release --example bench -- 500 2
//! (positional: <n_runs> <tolerance_cycles>)

use apex_sim::bench::{
canonical_suite,
run_sensitivity,
};

fn main() {
let args: Vec<String> = std::env::args().collect();
let n_runs: usize = args.get(1).and_then(|s| s.parse().ok()).unwrap_or(2000);
let tol: i64 = args.get(2).and_then(|s| s.parse().ok()).unwrap_or(2);

let suite = canonical_suite();
let mut rows = Vec::with_capacity(suite.len());
for (name, cfg) in &suite {
let report = run_sensitivity(cfg, n_runs, tol);
report.print(name);
println!();
rows.push((*name, report));
}

println!("=== summary (n={n_runs}, tol=±{tol} cycles) ===");
println!(
" {:<26} {:>7} {:>7} {:>8} {:>9}",
"scenario", "pass2%", "pass1%", "medErr", "us/run"
);
for (name, r) in &rows {
println!(
" {:<26} {:>7.1} {:>7.1} {:>8} {:>9.2}",
name,
r.pass2_pct(),
r.pass1_pct(),
r.median_err(),
r.score_us_per_run(),
);
}
}
Loading
Loading