rickub/ci-benchpublic Fork 0
1314292ca250a689fd63003465cb8e663c58d593
Commits
Clone
git clone https://git.rickub.com/rickub/ci-bench.git
git clone ssh://git@rickub.com/rickub/ci-bench.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

lib.rs · 95 lines · 3.1 KBRust Blame HistoryRaw
ci-bench: cross-platform CI race scaffold (rickub CI vs GitHub Actions) 0baf736 Olivier Girardot yesterday1//! bench-rust-build: the workload behind the ci-bench "rust build" steps.
2//!
3//! It deliberately touches every pinned dependency (serde + serde_json, tokio,
4//! clap, regex, anyhow) so a cold `cargo build` actually compiles the full
5//! transitive tree, and `cargo test` does real work on top of the warm build.
6
7use anyhow::Result;
8use clap::Parser;
9use serde::{Deserialize, Serialize};
10
11/// Tunable workload parameters (parsing exercises clap's derive machinery).
12#[derive(Debug, Clone, Serialize, Deserialize, Parser)]
13#[command(name = "bench-rust-build", about = "ci-bench synthetic workload")]
14pub struct Config {
15 /// Number of synthetic work items per round.
16 #[arg(long, default_value_t = 5000)]
17 pub items: u32,
18
19 /// Rounds to run.
20 #[arg(long, default_value_t = 4)]
21 pub rounds: u32,
22}
23
24impl Default for Config {
25 fn default() -> Self {
26 Self::parse_from(["bench-rust-build"])
27 }
28}
29
30/// A synthetic work item; JSON round-tripping exercises serde on a non-trivial
31/// type (string + vec + float + u64).
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub struct Item {
34 pub id: u64,
35 pub label: String,
36 pub tags: Vec<String>,
37 pub score: f64,
38}
39
40/// The label pattern that marks an item "interesting" (exercises regex).
41pub const LABEL_PATTERN: &str = r"^(cold|warm)-(cache|boot)-[0-9]{4}$";
42
43/// Matches labels against [`LABEL_PATTERN`].
44pub fn interesting(label: &str) -> bool {
45 // Compiled per call on purpose: the regex crate's compile path is part of
46 // the exercised cost, and the workload is small enough that it stays cheap.
47 let re = regex::Regex::new(LABEL_PATTERN).expect("static pattern compiles");
48 re.is_match(label)
49}
50
51/// Builds one synthetic item.
52pub fn make_item(round: u32, i: u32) -> Item {
53 let kind = if i % 2 == 0 { "cold-cache" } else { "warm-boot" };
54 Item {
55 id: (u64::from(round) << 32) | u64::from(i),
56 label: format!("{kind}-{:04}", i % 10_000),
57 tags: vec!["bench".to_string(), "ci-race".to_string()],
58 score: f64::from(i) * 0.5,
59 }
60}
61
62/// One round of synthetic work: build items, JSON round-trip each, select on a
63/// regex. Returns the number of selected items.
64pub fn run_round(cfg: &Config, round: u32) -> Result<usize> {
65 let mut selected = 0usize;
66 for i in 0..cfg.items {
67 let item = make_item(round, i);
68 let json = serde_json::to_string(&item)?;
69 let back: Item = serde_json::from_str(&json)?;
70 debug_assert_eq!(back, item);
71 if interesting(&back.label) {
72 selected += 1;
73 }
74 }
75 Ok(selected)
76}
77
78/// Tokio entry point: rounds on a multi-threaded runtime, briefly yielding
79/// between rounds so the scheduler paths are exercised too.
80pub async fn drive(cfg: Config) -> Result<usize> {
81 let mut total = 0usize;
82 for r in 0..cfg.rounds {
83 total += run_round(&cfg, r)?;
84 tokio::time::sleep(std::time::Duration::from_millis(1)).await;
85 }
86 Ok(total)
87}
88
89/// Synchronous entry point for the binary.
90pub fn run(cfg: Config) -> Result<usize> {
91 let rt = tokio::runtime::Builder::new_multi_thread()
92 .worker_threads(2)
93 .build()?;
94 rt.block_on(drive(cfg))
95}