1use std::path::Path;
7
8use serde::{Deserialize, Serialize};
9use silicera::hardware::HardwareInfo;
10use silicera::hnep::HnepProfile;
11use silicera::measure::MeasurementConfig;
12use silicera::repro::ReproPack;
13use silicera::Result;
14
15use crate::measure_alignment;
16use crate::arms::{run_domain_harness, ArmsHarnessConfig, HarnessSuiteReport};
17use crate::calm::{run_calm_check, CalmCheckReport};
18use crate::native_artifacts::{
19 default_artifact_kernels, run_native_artifact_suite, suite_to_workload_entries,
20 NativeArtifactSuite,
21};
22use crate::measure_placement;
23use crate::threads::measure_thread_count_sweep;
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct SingleMachineEval {
28 pub format: String,
30 pub version: u32,
32 pub host_brand: String,
34 pub fingerprint: Option<String>,
36 pub silicera_version: String,
38 pub calm: CalmCheckReport,
40 pub harness: HarnessSuiteReport,
42 pub artifacts: NativeArtifactSuite,
44 pub alignment_matters: bool,
46 pub alignment_note: String,
48 pub placement_fastest: String,
50 pub best_thread_count: usize,
52 pub threads_note: String,
54 pub deferred: Vec<String>,
56 pub summary: String,
58}
59
60pub fn run_single_machine_eval(
62 info: &HardwareInfo,
63 workspace: &Path,
64 harness_iters: usize,
65 artifact_iters: usize,
66) -> Result<SingleMachineEval> {
67 let calm = run_calm_check(info, 24)?;
68 let harness = run_domain_harness(
69 info,
70 &ArmsHarnessConfig {
71 warmup: 3,
72 iterations: harness_iters,
73 min_improvement: 0.03,
74 mem_target: "L2".into(),
75 mem_all_sizes: true,
76 },
77 "all",
78 )?;
79 let artifacts = run_native_artifact_suite(
80 info,
81 default_artifact_kernels(),
82 artifact_iters,
83 0.03,
84 workspace,
85 )?;
86 let align = measure_alignment(
87 info,
88 65_536,
89 &MeasurementConfig {
90 warmup: 2,
91 iterations: 12,
92 ..Default::default()
93 },
94 0.03,
95 )?;
96 let placement = measure_placement(
97 info,
98 4,
99 40_000,
100 &MeasurementConfig {
101 warmup: 2,
102 iterations: 10,
103 ..Default::default()
104 },
105 )?;
106 let threads = measure_thread_count_sweep(
107 info,
108 &MeasurementConfig {
109 warmup: 2,
110 iterations: 10,
111 ..Default::default()
112 },
113 80_000,
114 None,
115 )?;
116
117 let summary = format!(
118 "single-zen eval on {}: calm={} harness_portable_wins={} artifact_native_wins={} align_matters={} placement={} best_threads={}",
119 info.brand,
120 calm.calm,
121 harness.portable_win_count,
122 artifacts.native_win_count,
123 align.alignment_matters,
124 placement.fastest,
125 threads.best_candidate_threads
126 );
127
128 Ok(SingleMachineEval {
129 format: "silicera-single-machine-eval".into(),
130 version: 1,
131 host_brand: info.brand.clone(),
132 fingerprint: info.fingerprint.as_ref().map(|f| f.value.clone()),
133 silicera_version: silicera::VERSION.into(),
134 calm,
135 harness,
136 artifacts,
137 alignment_matters: align.alignment_matters,
138 alignment_note: align.recommendation,
139 placement_fastest: placement.fastest,
140 best_thread_count: threads.best_candidate_threads,
141 threads_note: threads.conclusion.clone(),
142 deferred: vec![
143 "Silicon Split Machine B / cross-machine YES-NO (requires second Zen host)".into(),
144 "Full PGO/BOLT measured tables (optional external toolchains)".into(),
145 ],
146 summary,
147 })
148}
149
150pub fn merge_artifacts_into_profile(
152 profile_path: &Path,
153 suite: &NativeArtifactSuite,
154) -> Result<HnepProfile> {
155 let mut profile = HnepProfile::read_from(profile_path)?;
156 for entry in suite_to_workload_entries(suite) {
157 profile.upsert_workload(entry)?;
158 }
159 profile.write_to(profile_path)?;
160 Ok(profile)
161}
162
163pub fn write_eval_outputs(
165 info: &HardwareInfo,
166 eval: &SingleMachineEval,
167 out_json: &Path,
168) -> Result<()> {
169 if let Some(parent) = out_json.parent() {
170 std::fs::create_dir_all(parent)?;
171 }
172 std::fs::write(out_json, serde_json::to_string_pretty(eval)?)?;
173 let repro = ReproPack::capture(
174 info,
175 "single-machine-eval",
176 serde_json::json!({
177 "output": out_json.display().to_string(),
178 "format": eval.format,
179 "version": eval.version,
180 }),
181 None,
182 None,
183 );
184 let repro_path = out_json.with_extension("repro.json");
185 repro.write_to(&repro_path)?;
186 Ok(())
187}