josie / alder-tools

//! Sweep presets — named workload sets + sweep params for the TUI's
//! picker screen + the `--preset` CLI arg. Each builder returns a
//! `Config` ready to hand to `Run::new`.
//!
//! Three presets:
//!  - `stability` — stress-ng-cpu → y-cruncher-sse → y-cruncher-avx2,
//!    5 min (300s) each, all cores, 15s cooldown, looped to fill ~1 hour
//!    (4 loops ≈ 49 min, 5 loops ≈ 61 min — we pick 5 to hit the target).
//!  - `stability_core_cycle` — the same three workloads, but run on ONE
//!    logical core at a time (cycling through all 16 twice), shorter
//!    per-core duration (default 60s). Used to test peak single-core
//!    clocks on chips with per-core load-based multipliers.
//!  - `bench` — 7zip-bench → y-cruncher-sse → y-cruncher-avx2, 60s each,
//!    all cores, 15s cooldown, single pass (the scores are the point;
//!    looping doesn't improve the measurement).

use crate::config::{Config, WorkloadSpec};

/// Workload names available (the from_spec match arms in workload.rs).
/// The custom editor iterates this list for its toggle rows.
pub const AVAILABLE_WORKLOADS: &[&str] = &[
    "stress-ng-cpu",
    "y-cruncher-sse",
    "y-cruncher-avx2",
    "7zip-bench",
    "c2c-latency",
    "dram-latency",
];

/// Cores range options for the custom editor's per-workload cores field.
/// P = performance cores only, E = efficiency cores only, All = both.
/// Enter on the Cores field cycles through this list (wired in ui.rs).
pub const CORES_OPTIONS: &[&str] = &["0-15", "0-11", "12-15"];

fn spec(name: &str, duration_secs: u64, cores: &str) -> WorkloadSpec {
    WorkloadSpec {
        name: name.to_string(),
        duration_secs,
        cores: cores.to_string(),
    }
}

/// Stability preset: cycle the three ISA-rail stress tests, 5 min each,
/// all cores, 15s cooldown, looped to fill ~1 hour. 4 loops = 49 min,
/// 5 loops = 61 min — pick 5 to cross the hour. Cancelable mid-run
/// (q in the TUI) so the user isn't committed to the full hour.
pub fn stability() -> Config {
    Config {
        workloads: vec![
            spec("stress-ng-cpu", 300, "0-15"),
            spec("y-cruncher-sse", 300, "0-15"),
            spec("y-cruncher-avx2", 300, "0-15"),
        ],
        cool_secs: 15,
        loop_count: 5,
        ..Config::default()
    }
}

/// Stability core-cycle preset: the same three workloads, but run on
/// ONE logical core at a time. Cycles through all 16 logical cores
/// twice. The `per_core_secs` arg sets the per-workload-per-core run
/// length (default 60s). Total run time ≈ 16 cores × 2 passes × 3
/// workloads × per_core_secs + cooldowns. Long, but cancelable.
///
/// This tests peak single-core clocks on chips with per-core load-based
/// multipliers — the question is whether each individual core is stable
/// at its own peak clock, not just whether the package is stable at the
/// all-core clock.
pub fn stability_core_cycle(per_core_secs: u64) -> Config {
    let mut workloads: Vec<WorkloadSpec> = Vec::new();
    // Two full passes across all 16 logical cores (0-15).
    for pass in 0..2u32 {
        for cpu in 0..16u32 {
            // Three ISA rails, each on this single core.
            for name in ["stress-ng-cpu", "y-cruncher-sse", "y-cruncher-avx2"] {
                workloads.push(spec(name, per_core_secs, &cpu.to_string()));
            }
        }
        let _ = pass; // pass count is the loop bound, not a body var
    }
    Config {
        workloads,
        cool_secs: 5,  // shorter cooldown — single-core load doesn't heat the package
        loop_count: 1, // the core-cycle IS the loop; don't loop the whole thing
        ..Config::default()
    }
}

/// Bench preset: the three score-producing workloads, 60s each, all
/// cores, 15s cooldown, single pass. The scores (7z rating MIPS,
/// y-cruncher FFTv4 Gb/s) are the comparative-perf signal across OC
/// configs.
pub fn bench() -> Config {
    Config {
        workloads: vec![
            spec("7zip-bench", 60, "0-15"),
            spec("y-cruncher-sse", 60, "0-15"),
            spec("y-cruncher-avx2", 60, "0-15"),
        ],
        cool_secs: 15,
        loop_count: 1,
        ..Config::default()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn stability_has_three_rails_five_loops() {
        let cfg = stability();
        assert_eq!(cfg.workloads.len(), 3);
        assert_eq!(cfg.workloads[0].name, "stress-ng-cpu");
        assert_eq!(cfg.workloads[1].name, "y-cruncher-sse");
        assert_eq!(cfg.workloads[2].name, "y-cruncher-avx2");
        assert_eq!(cfg.workloads[0].duration_secs, 300);
        assert_eq!(cfg.cool_secs, 15);
        assert_eq!(cfg.loop_count, 5);
    }

    #[test]
    fn stability_core_cycle_visits_each_core_twice() {
        let cfg = stability_core_cycle(60);
        // 16 cores × 2 passes × 3 workloads = 96 entries.
        assert_eq!(cfg.workloads.len(), 96);
        // First three are cpu0 (stress-ng, sse, avx2).
        assert_eq!(cfg.workloads[0].name, "stress-ng-cpu");
        assert_eq!(cfg.workloads[0].cores, "0");
        assert_eq!(cfg.workloads[1].name, "y-cruncher-sse");
        assert_eq!(cfg.workloads[1].cores, "0");
        assert_eq!(cfg.workloads[2].name, "y-cruncher-avx2");
        assert_eq!(cfg.workloads[2].cores, "0");
        // Last three are cpu15 on the second pass.
        let last = &cfg.workloads[95];
        assert_eq!(last.name, "y-cruncher-avx2");
        assert_eq!(last.cores, "15");
        // All single-core (no "0-15" range anywhere).
        assert!(cfg.workloads.iter().all(|w| w.cores != "0-15"));
        assert_eq!(cfg.loop_count, 1);
    }

    #[test]
    fn stability_core_cycle_custom_duration() {
        let cfg = stability_core_cycle(30);
        assert_eq!(cfg.workloads[0].duration_secs, 30);
    }

    #[test]
    fn bench_has_three_scored_workloads_single_pass() {
        let cfg = bench();
        assert_eq!(cfg.workloads.len(), 3);
        assert_eq!(cfg.workloads[0].name, "7zip-bench");
        assert_eq!(cfg.workloads[1].name, "y-cruncher-sse");
        assert_eq!(cfg.workloads[2].name, "y-cruncher-avx2");
        assert_eq!(cfg.workloads[0].duration_secs, 60);
        assert_eq!(cfg.loop_count, 1);
    }

    #[test]
    fn available_workloads_lists_v1_impls() {
        assert!(AVAILABLE_WORKLOADS.contains(&"stress-ng-cpu"));
        assert!(AVAILABLE_WORKLOADS.contains(&"y-cruncher-sse"));
        assert!(AVAILABLE_WORKLOADS.contains(&"y-cruncher-avx2"));
        assert!(AVAILABLE_WORKLOADS.contains(&"7zip-bench"));
        assert!(AVAILABLE_WORKLOADS.contains(&"c2c-latency"));
        assert!(AVAILABLE_WORKLOADS.contains(&"dram-latency"));
        // Deferred impl NOT in the list (minecraft).
        assert!(!AVAILABLE_WORKLOADS.contains(&"minecraft-server"));
    }
}