tangle


git clone https://radroots.dev/git/tangle.git
Log | Files | Refs | README | LICENSE

commit a548515e28abc1b5816204ddaea79e37a491958b
parent 0df82a8ec4535886b7ce39dd4895da725ab5f57d
Author: triesap <tyson@radroots.org>
Date:   Sun, 14 Jun 2026 19:33:20 -0700

bench: extend benchmark artifact schema

Diffstat:
Mcrates/tangle_bench/src/bin/tangle_benchmark_report.rs | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mcrates/tangle_bench/src/lib.rs | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 140 insertions(+), 9 deletions(-)

diff --git a/crates/tangle_bench/src/bin/tangle_benchmark_report.rs b/crates/tangle_bench/src/bin/tangle_benchmark_report.rs @@ -57,9 +57,17 @@ fn run() -> Result<Option<PathBuf>, String> { "count": supported_nips_count }); summary["run_identity"] = serde_json::json!({ - "git_commit": git_short_commit(), + "git_commit": git_full_commit(), + "git_commit_short": git_short_commit(), "rust_toolchain": rust_toolchain(), - "host_profile": host_profile() + "host_profile": host_profile(), + "os": env::consts::OS, + "arch": env::consts::ARCH + }); + summary["host_hardware"] = serde_json::json!({ + "cpu_model": cpu_model(), + "cpu_parallelism": cpu_parallelism(), + "memory_bytes": memory_bytes() }); let summary_path = artifact_dir.join("summary.json"); @@ -188,6 +196,10 @@ fn git_short_commit() -> String { command_text("git", &["rev-parse", "--short", "HEAD"]).unwrap_or_else(|| "unknown".to_owned()) } +fn git_full_commit() -> String { + command_text("git", &["rev-parse", "HEAD"]).unwrap_or_else(|| "unknown".to_owned()) +} + fn rust_toolchain() -> String { command_text("rustc", &["--version"]).unwrap_or_else(|| "unknown".to_owned()) } @@ -198,6 +210,45 @@ fn host_profile() -> String { format!("{os}-{arch}") } +fn cpu_model() -> String { + command_text("sysctl", &["-n", "machdep.cpu.brand_string"]) + .or_else(cpu_model_from_proc) + .unwrap_or_else(|| "unknown".to_owned()) +} + +fn cpu_parallelism() -> u64 { + std::thread::available_parallelism() + .map(|value| value.get().try_into().expect("parallelism fits in u64")) + .unwrap_or(0) +} + +fn memory_bytes() -> Option<u64> { + command_text("sysctl", &["-n", "hw.memsize"]) + .and_then(|value| value.parse::<u64>().ok()) + .or_else(memory_bytes_from_proc) +} + +fn cpu_model_from_proc() -> Option<String> { + let raw = fs::read_to_string("/proc/cpuinfo").ok()?; + raw.lines() + .find_map(|line| line.strip_prefix("model name")) + .and_then(|value| { + value + .split_once(':') + .map(|(_, model)| model.trim().to_owned()) + }) + .filter(|value| !value.is_empty()) +} + +fn memory_bytes_from_proc() -> Option<u64> { + let raw = fs::read_to_string("/proc/meminfo").ok()?; + raw.lines() + .find_map(|line| line.strip_prefix("MemTotal:")) + .and_then(|value| value.split_whitespace().next()) + .and_then(|value| value.parse::<u64>().ok()) + .and_then(|kib| kib.checked_mul(1024)) +} + fn command_text(command: &str, args: &[&str]) -> Option<String> { Command::new(command) .args(args) diff --git a/crates/tangle_bench/src/lib.rs b/crates/tangle_bench/src/lib.rs @@ -35,6 +35,8 @@ pub const SCENARIO_PROJECTION_REBUILD: &str = "projection_rebuild"; pub const SCENARIO_OUTBOX_REPLAY: &str = "outbox_replay"; pub const SCENARIO_BROADCAST_LAG: &str = "broadcast_lag"; pub const SCENARIO_MEMORY_PROFILE: &str = "memory_profile"; +pub const POCKET_SOURCE_REPOSITORY: &str = "https://github.com/triesap/pocket"; +pub const POCKET_SOURCE_REVISION: &str = "329334f20948c796c6016b673b92551ac4855ad7"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct BenchDatasetConfig { @@ -686,6 +688,7 @@ impl ScenarioReport { fn to_json(&self) -> serde_json::Value { json!({ "scenario": self.scenario, + "status": status(self.accepted == self.attempted && self.rejected == 0), "attempted": self.attempted, "accepted": self.accepted, "rejected": self.rejected, @@ -694,7 +697,15 @@ impl ScenarioReport { "p50_micros": self.p50_micros, "p95_micros": self.p95_micros, "p99_micros": self.p99_micros, - "max_rss_bytes": self.max_rss_bytes + "max_rss_bytes": self.max_rss_bytes, + "query_metrics": { + "candidates_scanned": self.attempted, + "events_returned": self.accepted, + "events_rejected": self.rejected + }, + "memory": { + "max_rss_bytes": self.max_rss_bytes + } }) } } @@ -938,15 +949,30 @@ impl BenchmarkRunReport { pub fn summary_json(&self, run_id: &str, artifact_directory: &Path) -> serde_json::Value { json!({ - "schema": 1, + "schema": 2, "run_id": run_id, "artifact_directory": artifact_directory.to_string_lossy(), "profile": self.profile.name().as_str(), "dataset": self.dataset_profile.to_json(), + "dataset_profile": self.dataset_profile.to_json(), "scenarios": self.scenarios.iter().map(ScenarioReport::to_json).collect::<Vec<_>>(), + "pocket_source": pocket_source_json(), "threshold_source": self.profile.threshold_source(), "thresholds": self.profile.thresholds().to_json(), "validation_summary": self.validation_summary, + "pass_fail_summary": { + "overall_status": validation_overall_status(&self.validation_summary), + "passed_scenarios": self + .validation_summary + .values() + .filter(|value| value.as_str() == "pass") + .count(), + "failed_scenarios": self + .validation_summary + .values() + .filter(|value| value.as_str() == "fail") + .count() + }, "proof_claim": { "eligible": self.profile.proof_claim_eligible(), "profile_required": "proof-*", @@ -963,6 +989,22 @@ impl BenchmarkRunReport { } } +fn pocket_source_json() -> serde_json::Value { + json!({ + "repository": POCKET_SOURCE_REPOSITORY, + "revision": POCKET_SOURCE_REVISION, + "crates": ["pocket-db", "pocket-types"] + }) +} + +fn validation_overall_status(summary: &BTreeMap<String, String>) -> &'static str { + if summary.values().all(|value| value == "pass") { + "pass" + } else { + "fail" + } +} + struct MaterializedBenchRelay { relay: BaseRelay, store_config: PocketStoreConfig, @@ -1892,10 +1934,11 @@ fn lower_hex(bytes: &[u8]) -> String { mod tests { use super::{ BenchDataset, BenchDatasetConfig, BenchGroupVisibility, BenchmarkProfile, - BenchmarkProfileName, BenchmarkRunReport, BenchmarkThresholds, SCENARIO_BROADCAST_LAG, - SCENARIO_COUNT_RESOURCE_CONTROLS, SCENARIO_GROUP_READ_GATE_OVERHEAD, - SCENARIO_MEMORY_PROFILE, SCENARIO_OUTBOX_REPLAY, SCENARIO_POCKET_QUERY_VISIBLE_EVENTS, - SCENARIO_PROJECTION_REBUILD, ScenarioReport, generated_state_counts, materialize_dataset, + BenchmarkProfileName, BenchmarkRunReport, BenchmarkThresholds, POCKET_SOURCE_REPOSITORY, + POCKET_SOURCE_REVISION, SCENARIO_BROADCAST_LAG, SCENARIO_COUNT_RESOURCE_CONTROLS, + SCENARIO_GROUP_READ_GATE_OVERHEAD, SCENARIO_MEMORY_PROFILE, SCENARIO_OUTBOX_REPLAY, + SCENARIO_POCKET_QUERY_VISIBLE_EVENTS, SCENARIO_PROJECTION_REBUILD, ScenarioReport, + generated_state_counts, materialize_dataset, }; use std::collections::BTreeSet; use tangle_groups::{GroupId, KIND_GROUP_ADMINS, KIND_GROUP_MEMBERS, KIND_GROUP_METADATA}; @@ -2246,21 +2289,58 @@ mod tests { .expect("report"); let summary = report.summary_json("unit-run", std::path::Path::new(".local/unit")); - assert_eq!(summary["schema"], 1); + assert_eq!(summary["schema"], 2); assert_eq!(summary["run_id"], "unit-run"); assert_eq!(summary["profile"], "smoke"); assert_eq!(summary["threshold_source"], "builtin:smoke"); + assert_eq!( + summary["pocket_source"]["repository"], + POCKET_SOURCE_REPOSITORY + ); + assert_eq!(summary["pocket_source"]["revision"], POCKET_SOURCE_REVISION); assert_eq!(summary["proof_claim"]["eligible"], false); assert_eq!(summary["proof_claim"]["target_hardware_evidence"], "absent"); assert_eq!( summary["dataset"]["fixture_family"], "synthetic repo-owned fixtures" ); + assert_eq!( + summary["dataset_profile"]["fixture_family"], + "synthetic repo-owned fixtures" + ); assert_eq!(summary["scenarios"].as_array().expect("scenarios").len(), 7); + let first_scenario = &summary["scenarios"] + .as_array() + .expect("scenarios") + .first() + .expect("first scenario"); + assert_eq!(first_scenario["status"], "pass"); + assert!(first_scenario["p50_micros"].as_u64().expect("p50") > 0); + assert!(first_scenario["p95_micros"].as_u64().expect("p95") > 0); + assert!(first_scenario["p99_micros"].as_u64().expect("p99") > 0); + assert!( + first_scenario["query_metrics"]["candidates_scanned"] + .as_u64() + .expect("candidates") + > 0 + ); + assert!( + first_scenario["query_metrics"]["events_returned"] + .as_u64() + .expect("returned") + > 0 + ); + assert!( + first_scenario["memory"]["max_rss_bytes"] + .as_u64() + .expect("memory") + > 0 + ); assert_eq!( summary["validation_summary"][SCENARIO_POCKET_QUERY_VISIBLE_EVENTS], "pass" ); + assert_eq!(summary["pass_fail_summary"]["overall_status"], "pass"); assert!( summary["thresholds"]["read_gate_p95_micros"] .as_u64()