1use std::collections::HashMap;
2#[cfg(not(target_arch = "wasm32"))]
3use std::sync::LazyLock;
4#[cfg(target_arch = "wasm32")]
5use std::sync::OnceLock;
6use dioxus::prelude::*;
7#[cfg(all(feature = "split", target_arch = "wasm32"))]
8use dioxus::wasm_split;
9use serde::Deserialize;
10use crate::ui::components::main_nav::{MainNav, ActivePage};
11use crate::ui::components::footer::Footer;
12use crate::ui::seo::{JsonLdMultiple, PageHead, organization_schema, breadcrumb_schema, webpage_schema, BreadcrumbItem, pages as seo_pages};
13
14fn config_from_toggles(toggles: &[bool]) -> logicaffeine_compile::optimization::OptimizationConfig {
17 let mut cfg = logicaffeine_compile::optimization::OptimizationConfig::all_off();
18 for (i, m) in logicaffeine_compile::optimization::REGISTRY.iter().enumerate() {
19 if toggles.get(i).copied().unwrap_or(false) {
20 cfg.set(m.opt, true);
21 }
22 }
23 cfg
24}
25
26fn toggles_from_config(cfg: &logicaffeine_compile::optimization::OptimizationConfig) -> Vec<bool> {
28 logicaffeine_compile::optimization::REGISTRY
29 .iter()
30 .map(|m| cfg.is_on(m.opt))
31 .collect()
32}
33
34#[derive(Deserialize)]
35struct BenchmarkData {
36 metadata: Metadata,
37 languages: Vec<Language>,
38 benchmarks: Vec<Benchmark>,
39 summary: SummaryData,
40}
41
42#[derive(Deserialize)]
43struct Metadata {
44 date: String,
45 commit: String,
46 logos_version: String,
47 cpu: String,
48 os: String,
49 #[serde(default)]
50 warmup: Option<u32>,
51 #[serde(default)]
52 runs: Option<u32>,
53 versions: HashMap<String, String>,
54}
55
56#[derive(Deserialize)]
57struct Language {
58 id: String,
59 label: String,
60 color: String,
61 tier: String,
62}
63
64#[derive(Deserialize)]
65struct Benchmark {
66 id: String,
67 name: String,
68 description: String,
69 reference_size: String,
70 sizes: Vec<String>,
71 logos_source: String,
72 generated_rust: String,
73 scaling: HashMap<String, HashMap<String, TimingResult>>,
74 compilation: HashMap<String, CompilationResult>,
75 #[serde(default)]
76 timeouts: HashMap<String, f64>,
77 #[serde(default)]
79 memory: Option<MemoryData>,
80 #[serde(default)]
83 binary_sizes: Option<BinarySizeData>,
84 #[serde(default)]
86 complexity: Option<Complexity>,
87 #[serde(default)]
90 fired: Vec<String>,
91 #[serde(default)]
93 blockers: Vec<(String, String)>,
94 #[serde(default)]
97 dependencies: Vec<(String, String)>,
98}
99
100#[derive(Deserialize, Clone)]
101struct MemoryData {
102 #[allow(dead_code)]
103 method: String,
104 by_size: HashMap<String, HashMap<String, Option<f64>>>,
106}
107
108#[derive(Deserialize, Clone)]
109struct BinarySizeData {
110 #[allow(dead_code)]
111 method: String,
112 by_language: HashMap<String, BinSizes>,
114}
115
116#[derive(Deserialize, Clone, Copy)]
119struct BinSizes {
120 as_built: f64,
122 #[serde(default)]
125 stripped: Option<f64>,
126}
127
128#[derive(Deserialize, Clone)]
129struct Complexity {
130 time: String,
131 space: String,
132}
133
134#[derive(Deserialize, Clone)]
135struct TimingResult {
136 mean_ms: f64,
137 median_ms: f64,
138 stddev_ms: f64,
139 min_ms: f64,
140 max_ms: f64,
141 cv: f64,
142 runs: u32,
143 #[serde(default)]
144 user_ms: Option<f64>,
145 #[serde(default)]
146 system_ms: Option<f64>,
147}
148
149#[derive(Deserialize, Clone)]
150struct CompilationResult {
151 mean_ms: f64,
152 stddev_ms: f64,
153}
154
155#[derive(Deserialize)]
156struct SummaryData {
157 geometric_mean_speedup_vs_c: HashMap<String, f64>,
158}
159
160fn empirical_exponent(points: &[(f64, f64)]) -> Option<f64> {
165 let pts: Vec<(f64, f64)> = points
166 .iter()
167 .filter(|&&(n, t)| n > 0.0 && t > 0.0)
168 .map(|&(n, t)| (n.ln(), t.ln()))
169 .collect();
170 if pts.len() < 2 {
171 return None;
172 }
173 let m = pts.len() as f64;
174 let sx: f64 = pts.iter().map(|p| p.0).sum();
175 let sy: f64 = pts.iter().map(|p| p.1).sum();
176 let sxx: f64 = pts.iter().map(|p| p.0 * p.0).sum();
177 let sxy: f64 = pts.iter().map(|p| p.0 * p.1).sum();
178 let denom = m * sxx - sx * sx;
179 if denom.abs() < 1e-12 {
180 return None; }
182 Some((m * sxy - sx * sy) / denom)
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn empirical_exponent_fits_linear() {
191 let pts: Vec<(f64, f64)> = [10.0, 100.0, 1000.0, 10000.0].iter().map(|&n| (n, 3.0 * n)).collect();
192 let b = empirical_exponent(&pts).expect("two+ points");
193 assert!((b - 1.0).abs() < 0.01, "linear data → exponent ~1.0, got {b}");
194 }
195
196 #[test]
197 fn empirical_exponent_fits_quadratic() {
198 let pts: Vec<(f64, f64)> = [10.0, 100.0, 1000.0].iter().map(|&n| (n, 2.0 * n * n)).collect();
199 let b = empirical_exponent(&pts).expect("two+ points");
200 assert!((b - 2.0).abs() < 0.01, "quadratic data → exponent ~2.0, got {b}");
201 }
202
203 #[test]
204 fn empirical_exponent_degenerate_is_none() {
205 assert!(empirical_exponent(&[(10.0, 5.0)]).is_none(), "one point → None");
206 assert!(empirical_exponent(&[]).is_none(), "no points → None");
207 assert!(empirical_exponent(&[(5.0, 1.0), (5.0, 9.0)]).is_none(), "all-equal n → None");
208 }
209
210 #[test]
211 fn linear_bar_pct_is_honest_and_proportional() {
212 assert!((linear_bar_pct(200.0, 200.0) - 100.0).abs() < 1e-9);
214 assert!((linear_bar_pct(50.0, 200.0) - 25.0).abs() < 1e-9);
216 assert!((linear_bar_pct(100.0, 200.0) - 50.0).abs() < 1e-9);
217 let a = linear_bar_pct(50.0, 200.0);
219 let b = linear_bar_pct(100.0, 200.0);
220 assert!((b / a - 2.0).abs() < 1e-9, "2x value → 2x bar, got {a} {b}");
221 assert!(
224 linear_bar_pct(0.070, 75.0) < 0.2,
225 "a ~1000x speed win must read as a sliver, not a third"
226 );
227 assert!((linear_bar_pct(2.544, 2.544) - 100.0).abs() < 1e-9);
229 let rust = linear_bar_pct(1.210, 2.544);
230 assert!((rust - 47.56).abs() < 0.1, "1.21/2.544 → ~47.6 %, got {rust}");
231 assert!(linear_bar_pct(500.0, 200.0) <= 100.0);
233 assert!(linear_bar_pct(5.0, 0.0) == 0.0);
234 }
235
236 #[test]
237 fn solver_bars_track_true_time_ratios() {
238 let d = &*SOLVER_DATA;
242 for fam in d.families.iter() {
243 for r in &fam.rows {
244 let scale = r.others.iter().map(|o| o.ms).fold(r.ours_ms, f64::max);
245 let ours = linear_bar_pct(r.ours_ms, scale);
246 for o in r.others.iter().filter(|o| o.status == "unsat" || o.status == "sat") {
247 let theirs = linear_bar_pct(o.ms, scale);
248 assert!(
250 ours <= theirs,
251 "[{}] n={}: ours {}ms bar {ours} must not exceed {} {}ms bar {theirs}",
252 fam.id, r.n, r.ours_ms, o.solver, o.ms
253 );
254 if ours > 0.5 && theirs > 0.5 {
256 let bar_ratio = theirs / ours;
257 let time_ratio = o.ms / r.ours_ms;
258 assert!(
259 (bar_ratio - time_ratio).abs() / time_ratio < 1e-6,
260 "[{}] n={}: bar ratio {bar_ratio} must equal time ratio {time_ratio}",
261 fam.id, r.n
262 );
263 }
264 }
265 }
266 }
267 }
268
269 #[test]
270 fn codec_bars_are_proportional_to_bytes() {
271 let d = &*CODEC_DATA;
274 for s in d.scenarios.iter().filter(|s| s.kind == "fair") {
275 let vmax = s.rows.iter().map(|r| r.size as f64).fold(0.0, f64::max);
276 let best = s.rows.iter().filter(|r| is_logos_codec(&r.codec)).map(|r| r.size).min();
277 let json = s.rows.iter().find(|r| r.codec == "json").map(|r| r.size);
278 if let (Some(best), Some(json)) = (best, json) {
279 if best < json {
280 let pb = linear_bar_pct(best as f64, vmax);
281 let pj = linear_bar_pct(json as f64, vmax);
282 assert!(pb < pj, "[{}] logos {best}B bar must be shorter than json {json}B", s.id);
283 assert!(
284 (pj / pb - json as f64 / best as f64).abs() < 1e-6,
285 "[{}] codec bar ratio must equal byte ratio", s.id
286 );
287 }
288 }
289 }
290 }
291
292 #[test]
293 fn solver_data_loads_and_we_win_where_claimed() {
294 let d = &*SOLVER_DATA; assert!(d.families.len() >= 3, "solver section must ship multiple families");
296 let php = d.families.iter().find(|f| f.id == "php").expect("php family present");
298 let walls = php.rows.iter().any(|r| {
299 r.others.iter().any(|o| o.solver == "z3" && o.status == "timeout")
300 && r.others.iter().any(|o| o.solver == "kissat" && o.status == "timeout")
301 });
302 assert!(walls, "PHP must show Z3 AND Kissat hitting the resolution wall");
303 for fam in d.families.iter().filter(|f| {
307 f.id == "php"
308 || f.id == "mutilated_chessboard"
309 || f.id == "tseitin"
310 || f.id.starts_with("mod_")
311 || f.id == "clique_coloring"
312 || f.id == "parity_cardinality"
313 || f.id == "count_q_mod3"
314 || f.id == "ordering_gt"
315 || f.id == "grid_tseitin"
316 }) {
317 for r in &fam.rows {
318 for o in &r.others {
319 if o.status == "unsat" || o.status == "sat" {
320 assert!(
321 r.ours_ms < o.ms,
322 "[{}] n={}: claimed a win but ours {}ms >= {} {}ms",
323 fam.id,
324 r.n,
325 r.ours_ms,
326 o.solver,
327 o.ms
328 );
329 }
330 }
331 }
332 }
333 }
334
335 #[test]
336 fn fmt_solver_ms_is_readable_and_never_zero() {
337 let zeros = ["0", "0 µs", "0.0 µs", "0.00 µs", "0.0 ms", "0.00 ms", "0.000 ms"];
339 let mut ms = 5_000.0_f64;
340 while ms > 1e-7 {
341 let s = fmt_solver_ms(ms);
342 assert!(s.contains("µs") || s.contains("ms") || s.contains(" s"), "no unit for {ms}: {s}");
343 assert!(!zeros.contains(&s.as_str()), "ms={ms} rendered as zero: {s}");
344 ms /= 1.7; }
346 assert_eq!(fmt_solver_ms(0.002), "2.0 µs");
348 assert_eq!(fmt_solver_ms(0.17), "170 µs");
349 assert_eq!(fmt_solver_ms(0.999), "999 µs");
350 assert_eq!(fmt_solver_ms(9.79), "9.8 ms");
351 assert_eq!(fmt_solver_ms(504.0), "504 ms");
352 assert_eq!(fmt_solver_ms(10_009.8), "10.0 s");
353 assert_eq!(fmt_solver_ms(0.0000004), "<0.01 µs");
354 for fam in &SOLVER_DATA.families {
356 for r in &fam.rows {
357 let s = fmt_solver_ms(r.ours_ms);
358 assert!(!zeros.contains(&s.as_str()), "[{}] n={} ours renders as zero: {s}", fam.id, r.n);
359 }
360 }
361 }
362
363 #[test]
364 fn benchmark_data_deserializes_without_memory_or_complexity() {
365 let json = r#"{"id":"x","name":"X","description":"d","reference_size":"1",
368 "sizes":["1"],"logos_source":"","generated_rust":"","scaling":{},
369 "compilation":{}}"#;
370 let b: Benchmark = serde_json::from_str(json).expect("deserialize without memory/complexity");
371 assert!(b.memory.is_none() && b.complexity.is_none() && b.binary_sizes.is_none());
372 }
373
374 #[test]
375 fn interp_schema_backward_and_forward_compatible() {
376 let t = |ms: f64| {
378 format!(
379 r#"{{"mean_ms":{ms},"median_ms":{ms},"stddev_ms":0.0,"min_ms":{ms},"max_ms":{ms},"cv":0.0,"runs":10}}"#
380 )
381 };
382 let old = format!(
385 r#"{{"metadata":{{"node":"v22","date":"x"}},
386 "benchmarks":[{{"id":"fib","name":"Fib","reference_size":"30",
387 "scaling":{{"30":{{"logos_interp":{li},"node":{nd}}}}}}}],
388 "summary":{{"geometric_mean_logos_interp_over_node":1.09}},"startup":{{}}}}"#,
389 li = t(2.0),
390 nd = t(1.0)
391 );
392 let d: InterpData = serde_json::from_str(&old).expect("old interp JSON must still load");
393 assert_eq!(d.summary.geometric_mean_logos_tiered_over_node, 0.0, "missing tiered geo defaults to 0");
394 assert!(d.interpreter_sizes.is_none(), "old interp JSON has no interpreter_sizes");
395
396 let new = format!(
399 r#"{{"metadata":{{"node":"v22","date":"x"}},
400 "benchmarks":[{{"id":"fib","name":"Fib","reference_size":"30",
401 "scaling":{{"30":{{"logos_interp":{li},"logos_tiered":{lt},"node":{nd}}}}}}}],
402 "summary":{{"geometric_mean_logos_interp_over_node":1.09,"geometric_mean_logos_tiered_over_node":0.98}},
403 "startup":{{}}}}"#,
404 li = t(2.0),
405 lt = t(1.5),
406 nd = t(1.0)
407 );
408 let d: InterpData = serde_json::from_str(&new).expect("new interp JSON must load");
409 assert!((d.summary.geometric_mean_logos_tiered_over_node - 0.98).abs() < 1e-9);
410 assert_eq!(d.summary.geometric_mean_logos_aot_over_node, 0.0, "missing AOT geo defaults to 0");
411 let scaling = &d.benchmarks[0].scaling["30"];
412 assert!(scaling.contains_key("logos_tiered"), "tiered engine row present");
413 assert!((scaling["logos_tiered"].mean_ms - 1.5).abs() < 1e-9);
414
415 let bundled = format!(
418 r#"{{"metadata":{{"node":"v22","date":"x"}},
419 "benchmarks":[{{"id":"fib","name":"Fib","reference_size":"30",
420 "scaling":{{"30":{{"logos_interp":{li},"logos_tiered":{lt},"logos_aot":{la},"node":{nd}}}}}}}],
421 "summary":{{"geometric_mean_logos_interp_over_node":1.09,"geometric_mean_logos_tiered_over_node":0.98,"geometric_mean_logos_aot_over_node":0.42}},
422 "startup":{{}}}}"#,
423 li = t(2.0),
424 lt = t(1.5),
425 la = t(0.6),
426 nd = t(1.0)
427 );
428 let d: InterpData = serde_json::from_str(&bundled).expect("bundled interp JSON must load");
429 assert!((d.summary.geometric_mean_logos_aot_over_node - 0.42).abs() < 1e-9);
430 let scaling = &d.benchmarks[0].scaling["30"];
431 assert!(scaling.contains_key("logos_aot"), "AOT engine row present when bundled");
432 assert!((scaling["logos_aot"].mean_ms - 0.6).abs() < 1e-9);
433 }
434
435 #[test]
441 fn every_benchmark_carries_binary_sizes() {
442 for b in &BENCH_DATA.benchmarks {
443 let sizes = b.binary_sizes.as_ref()
444 .unwrap_or_else(|| panic!("benchmark {} is missing binary_sizes", b.id));
445 assert!(!sizes.by_language.is_empty(), "benchmark {} has empty binary_sizes", b.id);
446 assert!(
447 sizes.by_language.contains_key("logos_release"),
448 "benchmark {} binary_sizes is missing the logos_release artifact", b.id
449 );
450 for (lang, s) in &sizes.by_language {
451 assert!(s.as_built > 0.0, "{}/{} as_built must be > 0, got {}", b.id, lang, s.as_built);
452 if let Some(st) = s.stripped {
453 assert!(
454 st > 0.0 && st <= s.as_built,
455 "{}/{} stripped ({st}) must be in (0, as_built={}]", b.id, lang, s.as_built
456 );
457 }
458 }
459 }
460 }
461
462 #[test]
463 fn interpreter_sizes_cover_logos_and_node() {
464 let sizes = INTERP_DATA.interpreter_sizes.as_ref()
465 .expect("latest-interp.json is missing interpreter_sizes");
466 for id in ["logos", "node"] {
467 let e = sizes.engines.get(id)
468 .unwrap_or_else(|| panic!("interpreter_sizes is missing the {id} engine"));
469 assert!(e.as_built > 0.0, "engine {id} as_built must be > 0, got {}", e.as_built);
470 if let Some(st) = e.stripped {
471 assert!(st > 0.0 && st <= e.as_built, "engine {id} stripped ({st}) out of range");
472 }
473 }
474 if let Some(w) = sizes.wasm_bundle_bytes {
475 assert!(w > 0.0, "wasm_bundle_bytes must be > 0 when present, got {w}");
476 }
477 }
478
479 #[test]
480 fn codec_data_deserializes_and_has_fair_scenarios() {
481 let d = &*CODEC_DATA;
482 assert_eq!(d.schema_version, 2, "schema_version is the page's compatibility gate");
483 let fair: Vec<_> = d.scenarios.iter().filter(|s| s.kind == "fair").collect();
484 assert!(!fair.is_empty(), "latest-codec.json must contain fair scenarios");
485 for s in &fair {
486 assert!(
487 s.rows.iter().any(|r| r.codec == "logos (BEST: all knobs)"),
488 "fair scenario '{}' is missing the BEST row",
489 s.id
490 );
491 }
492 assert!(
493 d.scenarios.iter().any(|s| s.kind == "random_access"),
494 "latest-codec.json must include the random-access scenario"
495 );
496 }
497
498 #[test]
499 fn codec_headline_is_provable() {
500 let (wins, total) = codec_size_wins(&CODEC_DATA);
503 assert!(total > 0, "expected fair workloads in latest-codec.json");
504 assert_eq!(wins, total, "the baked data must back the smallest-wire-on-all-workloads headline");
505 }
506
507 #[test]
508 fn codec_scorecard_fair_axes_all_win() {
509 let (enc, total) = codec_axis_wins(&CODEC_DATA, CodecAxis::Encode);
512 let (dec, _) = codec_axis_wins(&CODEC_DATA, CodecAxis::Decode);
513 assert!(total > 0);
514 assert_eq!(enc, total, "scorecard claims fastest encode on all fair workloads");
515 assert_eq!(dec, total, "scorecard claims fastest decode on all fair workloads");
516 assert!(codec_geomean_size(&CODEC_DATA) > 1.0, "geo-mean size advantage must exceed 1×");
518 assert!(codec_top_showcase(&CODEC_DATA).is_some_and(|r| r > 10.0), "a showcase must crush by >10×");
519 }
520
521 #[test]
522 fn format_bytes_scales_units() {
523 assert_eq!(format_bytes(16_120.0), "16 KB");
524 assert_eq!(format_bytes(2.0 * 1024.0 * 1024.0), "2.0 MB");
525 assert_eq!(format_bytes(3.0 * 1024.0 * 1024.0 * 1024.0), "3.0 GB");
526 }
527
528 #[test]
529 fn footprint_label_only_tags_a_distinct_stripped_size() {
530 assert_eq!(footprint_label(2_033_384.0, Some(307_280.0)), "1.9 MB (300 KB stripped)");
532 assert_eq!(footprint_label(334_592.0, Some(334_584.0)), "327 KB");
534 assert_eq!(footprint_label(583.0, None), "1 KB");
536 assert_eq!(footprint_label(1000.0, Some(2000.0)), format_bytes(1000.0));
538 }
539
540 #[test]
541 fn shipped_footprint_prefers_the_deployable_stripped_size() {
542 assert_eq!(shipped_bytes(2_033_384.0, Some(307_280.0)), 307_280.0);
545 assert_eq!(shipped_footprint_label(2_033_384.0, Some(307_280.0)), "300 KB (1.9 MB as-built)");
546 assert_eq!(shipped_bytes(334_592.0, Some(334_584.0)), 334_584.0);
549 assert_eq!(shipped_footprint_label(334_592.0, Some(334_584.0)), "327 KB (327 KB as-built)");
550 assert_eq!(shipped_bytes(583.0, None), 583.0);
552 assert_eq!(shipped_footprint_label(583.0, None), "1 KB (1 KB as-built)");
553 assert_eq!(shipped_bytes(1000.0, Some(2000.0)), 1000.0);
555 }
556
557 #[test]
558 fn every_language_bar_uses_a_stripped_or_fallback_shipped_size() {
559 for b in BENCH_DATA.benchmarks.iter() {
563 let Some(sizes) = b.binary_sizes.as_ref() else { continue };
564 for (lang, s) in sizes.by_language.iter() {
565 let shipped = shipped_bytes(s.as_built, s.stripped);
566 assert!(shipped > 0.0 && shipped <= s.as_built,
567 "{}/{lang}: shipped {shipped} must be in (0, as_built={}]", b.id, s.as_built);
568 if let Some(st) = s.stripped {
569 if st > 0.0 && st <= s.as_built {
570 assert_eq!(shipped, st, "{}/{lang}: must ship the stripped size", b.id);
571 }
572 }
573 }
574 }
575 }
576
577 #[test]
578 fn every_codec_scenario_has_a_good_for_line() {
579 for s in CODEC_DATA.scenarios.iter() {
582 assert!(codec_good_for(&s.id).is_some(), "scenario '{}' has no codec_good_for copy", s.id);
583 }
584 }
585
586 #[test]
587 fn every_fair_best_row_names_the_dials_that_won() {
588 for s in CODEC_DATA.scenarios.iter().filter(|s| s.kind == "fair") {
591 let best = codec_best_row(s).unwrap_or_else(|| panic!("fair '{}' missing the BEST row", s.id));
592 let chosen = best.chosen.as_ref().unwrap_or_else(|| panic!("fair '{}' BEST row missing chosen", s.id));
593 assert!(!chosen.summary.trim().is_empty(), "fair '{}' chosen.summary is empty", s.id);
594 assert!(!chosen.columns.is_empty(), "fair '{}' chosen.columns is empty", s.id);
595 }
596 }
597
598 #[test]
599 fn every_fair_scenario_has_a_fair_fight() {
600 for s in CODEC_DATA.scenarios.iter().filter(|s| s.kind == "fair") {
603 let (lo, co, _) = codec_fair_fight(s)
604 .unwrap_or_else(|| panic!("fair '{}' has no fair-fight (competitors missing fair_size)", s.id));
605 assert!(lo > 0 && co > 0, "fair '{}' fair-fight sizes must be positive", s.id);
606 }
607 let (wins, total) = codec_fair_size_wins(&CODEC_DATA);
609 assert!(total > 0 && wins <= total, "fair-fight count must be a valid n/{total}");
610 }
611
612 #[test]
613 fn sources_cover_every_benchmark() {
614 for b in BENCH_DATA.benchmarks.iter() {
618 let s = bench_sources_for(&b.id)
619 .unwrap_or_else(|| panic!("benchmark '{}' has no sources entry", b.id));
620 for (lang, src) in [
621 ("c", s.c), ("cpp", s.cpp), ("rust", s.rust), ("zig", s.zig), ("go", s.go),
622 ("java", s.java), ("js", s.js), ("python", s.python), ("ruby", s.ruby),
623 ("nim", s.nim), ("logos", s.logos),
624 ] {
625 assert!(!src.trim().is_empty(), "benchmark '{}' source '{lang}' is empty", b.id);
626 }
627 }
628 }
629}
630
631#[cfg(not(target_arch = "wasm32"))]
636static BENCH_DATA: LazyLock<BenchmarkData> = LazyLock::new(|| {
637 serde_json::from_str(include_str!("../../../../../benchmarks/results/latest.json")).unwrap()
638});
639#[cfg(target_arch = "wasm32")]
640static BENCH_DATA: OnceLock<BenchmarkData> = OnceLock::new();
641
642fn bench_data() -> Option<&'static BenchmarkData> {
645 #[cfg(not(target_arch = "wasm32"))]
646 {
647 Some(&BENCH_DATA)
648 }
649 #[cfg(target_arch = "wasm32")]
650 {
651 BENCH_DATA.get()
652 }
653}
654
655#[derive(Deserialize)]
660struct SolverData {
661 #[serde(default)]
662 metadata: SolverMeta,
663 families: Vec<SolverFamily>,
664}
665
666#[derive(Deserialize, Default)]
667struct SolverMeta {
668 #[serde(default)]
669 date: String,
670 #[serde(default)]
671 cpu: String,
672 #[serde(default)]
673 kissat: String,
674 #[serde(default)]
675 sadical: String,
676 #[serde(default)]
677 cadical: String,
678 #[serde(default)]
679 cryptominisat: String,
680}
681
682#[derive(Deserialize)]
683struct SolverFamily {
684 id: String,
685 name: String,
686 mechanism: String,
688 separation: String,
690 note: String,
692 rows: Vec<SolverRow>,
693}
694
695#[derive(Deserialize)]
696struct SolverRow {
697 n: u32,
698 ours_ms: f64,
699 #[serde(default)]
700 ours_min_ms: f64,
701 ours_detail: String,
702 #[serde(default)]
706 ours_proof_bytes: Option<u64>,
707 #[serde(default)]
709 ours_proof_fmt: Option<String>,
710 #[serde(default)]
713 others: Vec<OtherResult>,
714}
715
716#[derive(Deserialize)]
719struct OtherResult {
720 solver: String,
721 status: String,
722 ms: f64,
723 #[serde(default)]
724 proof_bytes: Option<u64>,
725}
726
727#[cfg(not(target_arch = "wasm32"))]
728static SOLVER_DATA: LazyLock<SolverData> = LazyLock::new(|| {
729 serde_json::from_str(include_str!("../../../../../benchmarks/results/solvers.json")).unwrap()
730});
731#[cfg(target_arch = "wasm32")]
732static SOLVER_DATA: OnceLock<SolverData> = OnceLock::new();
733
734fn solver_data() -> Option<&'static SolverData> {
736 #[cfg(not(target_arch = "wasm32"))]
737 {
738 Some(&SOLVER_DATA)
739 }
740 #[cfg(target_arch = "wasm32")]
741 {
742 SOLVER_DATA.get()
743 }
744}
745
746fn fmt_solver_ms(ms: f64) -> String {
751 if !(ms > 0.0) {
752 return "0".to_string();
753 }
754 if ms < 1.0 {
755 let us = ms * 1000.0;
756 if us < 0.01 {
757 "<0.01 µs".to_string()
758 } else if us < 1.0 {
759 format!("{us:.2} µs")
760 } else if us < 10.0 {
761 format!("{us:.1} µs")
762 } else {
763 format!("{us:.0} µs")
764 }
765 } else if ms < 100.0 {
766 format!("{ms:.1} ms")
767 } else if ms < 10_000.0 {
768 format!("{ms:.0} ms")
769 } else {
770 format!("{:.1} s", ms / 1000.0)
771 }
772}
773
774fn linear_bar_pct(value: f64, max: f64) -> f64 {
780 if max <= 0.0 {
781 return 0.0;
782 }
783 (value / max * 100.0).clamp(0.0, 100.0)
784}
785
786fn solver_color(name: &str) -> &'static str {
788 match name {
789 "z3" => "#a78bfa",
790 "kissat" => "#fb923c",
791 "sadical" => "#9ca3af",
792 "cadical" => "#f472b6",
793 "cryptominisat" => "#34d399",
794 _ => "#6b7280",
795 }
796}
797
798fn solver_label(name: &str) -> String {
800 match name {
801 "z3" => "Z3".to_string(),
802 "kissat" => "Kissat".to_string(),
803 "sadical" => "SaDiCaL".to_string(),
804 "cadical" => "CaDiCaL".to_string(),
805 "cryptominisat" => "CryptoMiniSat".to_string(),
806 other => other.to_string(),
807 }
808}
809
810fn solver_bar(label: &str, pct: f64, color: &str, time: &str, detail: &str) -> Element {
813 let show_inside = pct > 34.0;
814 rsx! {
815 div { class: "bench-bar-row",
816 div { class: "bench-bar-label", "{label}" }
817 div { class: "bench-bar-track",
818 div {
819 class: "bench-bar-fill",
820 style: "width: {pct:.1}%; background: {color};",
821 title: "{detail}",
822 if show_inside {
823 span { class: "bench-bar-time", "{time}" }
824 }
825 }
826 }
827 if !show_inside {
828 span { class: "bench-bar-time-outside", "{time}" }
829 }
830 }
831 }
832}
833
834#[component]
841pub fn SolverSection() -> Element {
842 let data = solver_data().expect("rendered only inside the loaded Benchmarks page");
843 let meta = &data.metadata;
844 let php = data.families.iter().find(|f| f.id == "php");
846 let head = php.and_then(|f| {
847 let r0 = f.rows.first()?;
848 let walled: Vec<String> = r0
849 .others
850 .iter()
851 .filter(|o| o.status == "timeout")
852 .map(|o| solver_label(&o.solver))
853 .collect();
854 let sad_max = f
855 .rows
856 .iter()
857 .filter(|r| r.ours_ms > 0.0)
858 .filter_map(|r| {
859 r.others
860 .iter()
861 .find(|o| o.solver == "sadical" && o.status == "unsat")
862 .map(|s| s.ms / r.ours_ms)
863 })
864 .fold(0.0_f64, f64::max);
865 Some((r0.n, fmt_solver_ms(r0.ours_ms), walled, sad_max))
866 });
867
868 rsx! {
869 div { class: "bench-section", id: "solver",
870 div { class: "bench-section-title", "Our solver vs the field" }
871 div { class: "bench-section-desc",
872 "Raw speed is one axis; reasoning is another. On the classic structured-UNSAT families, our pure-Rust certified prover decides in milliseconds what brute-force search cannot — the same engine that runs in the Studio, in your browser, with no Z3. We measure it on a byte-identical formula against Z3 (SMT), Kissat (the CDCL world champion) and SaDiCaL (the reference PR/SDCL solver). Every \u{201c}ours\u{201d} verdict is a re-checked certificate, not a trusted answer."
873 }
874
875 if let Some((n, ours, walled, sad_max)) = head {
876 div {
877 style: "background: linear-gradient(135deg, rgba(34,211,238,0.10), rgba(167,139,250,0.10)); border: 1px solid rgba(34,211,238,0.25); border-radius: 12px; padding: 16px 18px; margin-bottom: 22px;",
878 div { style: "font-size: 15px; font-weight: 700; color: #e5e7eb; margin-bottom: 4px;",
879 "A proof-system gap, not a tuning trick"
880 }
881 div { style: "font-size: 13px; color: rgba(229,231,235,0.7); line-height: 1.5;",
882 {
883 let walled_str = if walled.is_empty() { "Z3 and Kissat".to_string() } else { walled.join(" and ") };
884 rsx! {
885 "{walled_str} can't even finish PHP({n}) — they hit the resolution wall every CDCL solver inherits (Haken 1985, 2^\u{03a9}(n)). We certify it in {ours}"
886 if sad_max >= 1.5 {
887 ", and beat SaDiCaL — the reference PR/SDCL solver, in our own proof class — by up to {sad_max:.0}\u{00d7}, with a kilobyte-scale certificate against its megabytes"
888 }
889 ". Our SDCL even "
890 em { "discovers" }
891 " that proof from the raw clauses with zero hints."
892 }
893 }
894 }
895 }
896 }
897
898 for fam in data.families.iter() {
899 div {
900 style: "margin-bottom: 26px;",
901 div { style: "font-size: 15px; font-weight: 700; color: #fff;", "{fam.name}" }
902 div { style: "font-size: 12px; color: rgba(34,211,238,0.85); margin: 2px 0 2px;", "ours: {fam.mechanism}" }
903 div { style: "font-size: 12px; color: rgba(229,231,235,0.55); margin-bottom: 12px;", "{fam.separation}" }
904 div { class: "bench-chart",
905 for row in fam.rows.iter() {
906 div { style: "margin-bottom: 12px;",
907 div { style: "font-size: 11px; color: rgba(229,231,235,0.5); font-family: ui-monospace, monospace; margin-bottom: 2px;", "n = {row.n}" }
908 {
909 let scale_max = row.others.iter().map(|o| o.ms).fold(row.ours_ms, f64::max);
914 rsx! {
915 div { class: "bench-chart",
916 {
917 let mut ours_lbl = fmt_solver_ms(row.ours_ms);
922 if let Some(pb) = row.ours_proof_bytes {
923 let fmt = row.ours_proof_fmt.as_deref().unwrap_or("proof");
924 ours_lbl = format!("{ours_lbl} · {} {fmt}", format_bytes(pb as f64));
925 }
926 solver_bar("ours", linear_bar_pct(row.ours_ms, scale_max), "#22d3ee", &ours_lbl, &row.ours_detail)
927 }
928 for o in row.others.iter() {
929 {
930 let (pct, color, lbl) = match o.status.as_str() {
931 "timeout" => (100.0_f64, "#ef4444", format!("timeout ≥{}", fmt_solver_ms(o.ms))),
932 "unsat" | "sat" => {
933 let mut l = fmt_solver_ms(o.ms);
934 if let Some(pb) = o.proof_bytes {
935 l = format!("{l} · {} proof", format_bytes(pb as f64));
936 }
937 (linear_bar_pct(o.ms, scale_max), solver_color(&o.solver), l)
938 }
939 other => (1.0_f64, "#475569", other.to_string()),
940 };
941 solver_bar(&solver_label(&o.solver), pct, color, &lbl, &format!("{} — solve + proof", solver_label(&o.solver)))
942 }
943 }
944 }
945 }
946 }
947 }
948 }
949 }
950 div { style: "font-size: 11.5px; color: rgba(229,231,235,0.5); line-height: 1.5; margin-top: 8px;", "{fam.note}" }
951 }
952 }
953
954 div { style: "font-size: 11px; color: rgba(229,231,235,0.4); line-height: 1.5; border-top: 1px solid rgba(255,255,255,0.08); padding-top: 12px;",
955 "Method: Kissat, SaDiCaL, CaDiCaL and CryptoMiniSat run as subprocesses on the byte-identical DIMACS, each emitting a clausal proof to disk whose size is shown (solve + certify, like ours); Z3 is the in-process oracle (no proof). Our own proof size is the size of our certified artifact — the SR proof for the pigeonhole class, the compact GF/ℤ-ring linear certificate for the algebraic families — measured by streaming it through a byte counter in memory, never written to disk and capped so it can never itself exhaust RAM. CryptoMiniSat runs in its default DRAT-emitting config; with its GF(2) Gaussian enabled it would decide parity fast but then could not emit a standard clausal proof — the certified gap. Ours is the median of 5 release runs (identical to the WASM build). Timeouts: Z3 10 s, Kissat/CaDiCaL/CryptoMiniSat 15 s, SaDiCaL 45 s (generous, so the PR solver runs to completion — no inflation). Sub-millisecond times are dominated by external process startup, so the headline claims rest on the cases where a competitor takes seconds or hits the wall. Random 3-SAT is the honesty control."
956 if !meta.cpu.is_empty() {
957 span { " · Measured on {meta.cpu}" }
958 }
959 if !meta.kissat.is_empty() {
960 span { " · Kissat {meta.kissat}" }
961 }
962 if !meta.cadical.is_empty() {
963 span { " · CaDiCaL {meta.cadical}" }
964 }
965 if !meta.cryptominisat.is_empty() {
966 span { " · CryptoMiniSat {meta.cryptominisat}" }
967 }
968 if !meta.date.is_empty() {
969 span { " · {meta.date}" }
970 }
971 }
972 }
973 }
974}
975
976#[cfg(test)]
977mod solver_data_guard {
978 use super::*;
979
980 #[test]
981 fn solver_data_parses_and_carries_our_proof_size() {
982 let data = &*SOLVER_DATA;
985 assert!(!data.families.is_empty(), "solvers.json must have families");
986
987 let php = data.families.iter().find(|f| f.id == "php").expect("php family present");
989 assert!(
990 php.rows.iter().all(|r| r.ours_proof_bytes.is_some() && r.ours_proof_fmt.is_some()),
991 "every PHP row must carry our own SR proof size"
992 );
993
994 for id in ["tseitin", "mod_3_tseitin", "mod_6_tseitin", "php_variants", "mutilated_chessboard"] {
998 let fam = data.families.iter().find(|f| f.id == id).unwrap_or_else(|| panic!("{id} present"));
999 assert!(
1000 fam.rows.iter().all(|r| r.ours_proof_bytes.is_some()),
1001 "the {id} family must carry a proof size on every row"
1002 );
1003 }
1004
1005 let random = data.families.iter().find(|f| f.id == "random_3sat").expect("random present");
1008 assert!(
1009 random.rows.iter().any(|r| r.ours_detail.contains("unsat") && r.ours_proof_bytes.is_some()),
1010 "the random control's UNSAT row must carry a DRAT proof size"
1011 );
1012
1013 let solvers: std::collections::HashSet<&str> = data
1015 .families
1016 .iter()
1017 .flat_map(|f| f.rows.iter())
1018 .flat_map(|r| r.others.iter())
1019 .map(|o| o.solver.as_str())
1020 .collect();
1021 assert!(solvers.contains("cadical"), "CaDiCaL must appear in the field");
1022 }
1023}
1024
1025#[derive(Deserialize)]
1029struct CodecData {
1030 #[serde(default)]
1031 schema_version: u32,
1032 #[serde(default)]
1033 metadata: CodecMetadata,
1034 #[serde(default)]
1035 #[allow(dead_code)]
1036 iters: u32,
1037 #[serde(default)]
1038 scenarios: Vec<CodecScenario>,
1039}
1040
1041#[derive(Deserialize, Default)]
1042struct CodecMetadata {
1043 #[serde(default)]
1044 date: String,
1045 #[serde(default)]
1046 #[allow(dead_code)]
1047 commit: String,
1048 #[serde(default)]
1049 #[allow(dead_code)]
1050 logos_version: String,
1051 #[serde(default)]
1052 cpu: String,
1053 #[serde(default)]
1054 os: String,
1055 #[serde(default)]
1056 versions: HashMap<String, String>,
1057 #[serde(default)]
1058 features: Vec<String>,
1059}
1060
1061#[derive(Deserialize, Clone)]
1062struct CodecScenario {
1063 id: String,
1064 title: String,
1065 #[serde(default)]
1066 n: usize,
1067 kind: String,
1068 rows: Vec<CodecRow>,
1069}
1070
1071#[derive(Deserialize, Clone)]
1072struct CodecRow {
1073 codec: String,
1074 size: usize,
1075 #[serde(default)]
1076 enc_ns: f64,
1077 #[serde(default)]
1078 dec_ns: f64,
1079 #[serde(default)]
1080 read_one_ns: Option<f64>,
1081 #[serde(default)]
1082 chosen: Option<ChosenConfig>,
1083 #[serde(default)]
1087 fair_size: Option<usize>,
1088}
1089
1090#[derive(Deserialize, Clone)]
1095struct ChosenConfig {
1096 #[serde(default)]
1097 #[allow(dead_code)]
1098 numerics: String,
1099 #[serde(default)]
1100 #[allow(dead_code)]
1101 floats: String,
1102 #[serde(default)]
1103 #[allow(dead_code)]
1104 compression: String,
1105 #[serde(default)]
1106 #[allow(dead_code)]
1107 columns: Vec<String>,
1108 #[serde(default)]
1109 summary: String,
1110}
1111
1112#[cfg(not(target_arch = "wasm32"))]
1113static CODEC_DATA: LazyLock<CodecData> = LazyLock::new(|| {
1114 serde_json::from_str(include_str!("../../../../../benchmarks/results/latest-codec.json")).unwrap()
1115});
1116#[cfg(target_arch = "wasm32")]
1117static CODEC_DATA: OnceLock<CodecData> = OnceLock::new();
1118
1119fn codec_data() -> Option<&'static CodecData> {
1121 #[cfg(not(target_arch = "wasm32"))]
1122 {
1123 Some(&CODEC_DATA)
1124 }
1125 #[cfg(target_arch = "wasm32")]
1126 {
1127 CODEC_DATA.get()
1128 }
1129}
1130
1131fn is_logos_codec(c: &str) -> bool {
1134 c.starts_with("logos")
1135}
1136
1137fn codec_label(c: &str) -> &str {
1138 match c {
1139 "logos (BEST: all knobs)" => "LOGOS \u{2014} all knobs",
1140 "logos (varint)" => "LOGOS \u{2014} varint",
1141 "logos (fixed)" => "LOGOS \u{2014} fixed (memcpy)",
1142 "logos (gv/simd)" => "LOGOS \u{2014} group-varint",
1143 "logos (affine)" => "LOGOS \u{2014} affine",
1144 "logos (struct-view)" => "LOGOS \u{2014} struct-view",
1145 "logos (struct-view fixed)" => "LOGOS \u{2014} struct-view (fixed)",
1146 "logos (zero-copy)" => "LOGOS \u{2014} zero-copy",
1147 "bincode" => "bincode",
1148 "postcard" => "postcard",
1149 "messagepack" => "MessagePack",
1150 "cbor" => "CBOR",
1151 "json" => "JSON",
1152 "arrow (ipc)" => "Apache Arrow",
1153 "protobuf/grpc" => "Protobuf / gRPC",
1154 "capnproto" => "Cap'n Proto",
1155 other => other,
1156 }
1157}
1158
1159fn codec_color(c: &str) -> &'static str {
1160 match c {
1161 "logos (BEST: all knobs)" => "#00d4ff",
1162 "logos (struct-view)" => "#00d4ff",
1163 "logos (struct-view fixed)" => "#26c6da",
1164 "logos (zero-copy)" => "#22d3ee",
1165 "logos (varint)" => "#0098c4",
1166 "logos (fixed)" => "#4dd0e1",
1167 "logos (gv/simd)" => "#26c6da",
1168 "logos (affine)" => "#80deea",
1169 "bincode" => "#dea584",
1170 "postcard" => "#f7a41d",
1171 "messagepack" => "#c0392b",
1172 "cbor" => "#8e44ad",
1173 "json" => "#f7df1e",
1174 "arrow (ipc)" => "#00ADD8",
1175 "protobuf/grpc" => "#4285f4",
1176 "capnproto" => "#e67e22",
1177 _ => "#94a3b8",
1178 }
1179}
1180
1181fn format_ns(ns: f64) -> String {
1184 if ns < 1000.0 {
1185 format!("{ns:.0} ns")
1186 } else if ns < 1_000_000.0 {
1187 format!("{:.2} \u{00b5}s", ns / 1000.0)
1188 } else {
1189 format!("{:.2} ms", ns / 1_000_000.0)
1190 }
1191}
1192
1193#[derive(Clone, Copy, PartialEq)]
1194enum CodecAxis {
1195 Size,
1196 Encode,
1197 Decode,
1198 ReadOne,
1199}
1200
1201fn codec_axis_chart(s: &CodecScenario, axis: CodecAxis) -> Element {
1205 let val = |r: &CodecRow| -> Option<f64> {
1206 match axis {
1207 CodecAxis::Size => Some(r.size as f64),
1208 CodecAxis::Encode => Some(r.enc_ns),
1209 CodecAxis::Decode => Some(r.dec_ns),
1210 CodecAxis::ReadOne => r.read_one_ns,
1211 }
1212 };
1213 let fmt = |r: &CodecRow| -> String {
1214 match axis {
1215 CodecAxis::Size => format_bytes(r.size as f64),
1216 _ => format_ns(val(r).unwrap_or(0.0)),
1217 }
1218 };
1219 let mut rows: Vec<CodecRow> = s.rows.iter().filter(|r| val(r).is_some()).cloned().collect();
1220 rows.sort_by(|a, b| val(a).unwrap().partial_cmp(&val(b).unwrap()).unwrap_or(std::cmp::Ordering::Equal));
1221 if rows.is_empty() {
1222 return rsx! {};
1223 }
1224 let vmax = val(rows.last().unwrap()).unwrap();
1227 rsx! {
1228 div { class: "bench-chart",
1229 for r in rows.iter() {
1230 {
1231 let v = val(r).unwrap();
1232 let pct = linear_bar_pct(v, vmax);
1233 let s_str = fmt(r);
1234 let fill_class = if is_logos_codec(&r.codec) { "bench-codec-fill logos-highlight" } else { "bench-codec-fill" };
1235 let val_class = if is_logos_codec(&r.codec) { "bench-codec-val logos-val" } else { "bench-codec-val" };
1236 let color = codec_color(&r.codec);
1237 let label = codec_label(&r.codec);
1238 rsx! {
1239 div { class: "bench-bar-row",
1240 div { class: "bench-bar-label", "{label}" }
1241 div { class: "bench-codec-track",
1242 div { class: "{fill_class}", style: "width: {pct:.1}%; background: {color};" }
1243 }
1244 div { class: "{val_class}", "{s_str}" }
1245 }
1246 }
1247 }
1248 }
1249 }
1250 }
1251}
1252
1253fn codec_size_wins(d: &CodecData) -> (usize, usize) {
1258 let fair: Vec<&CodecScenario> = d.scenarios.iter().filter(|s| s.kind == "fair").collect();
1259 let wins = fair
1260 .iter()
1261 .filter(|s| match s.rows.iter().find(|r| r.codec == "logos (BEST: all knobs)") {
1262 Some(best) => s.rows.iter().filter(|r| !is_logos_codec(&r.codec)).all(|r| best.size <= r.size),
1263 None => false,
1264 })
1265 .count();
1266 (wins, fair.len())
1267}
1268
1269fn codec_best_vs_competitor(s: &CodecScenario) -> Option<(usize, String, usize)> {
1272 let ours = s.rows.iter().filter(|r| is_logos_codec(&r.codec)).map(|r| r.size).min()?;
1273 let rival = s.rows.iter().filter(|r| !is_logos_codec(&r.codec)).min_by_key(|r| r.size)?;
1274 Some((ours, codec_label(&rival.codec).to_string(), rival.size))
1275}
1276
1277fn codec_random_access_vs_capnp(s: &CodecScenario) -> Option<(f64, f64)> {
1279 let ours = s
1280 .rows
1281 .iter()
1282 .filter(|r| is_logos_codec(&r.codec))
1283 .filter_map(|r| r.read_one_ns)
1284 .fold(f64::INFINITY, f64::min);
1285 let capnp = s.rows.iter().find(|r| r.codec == "capnproto").and_then(|r| r.read_one_ns)?;
1286 if ours.is_finite() {
1287 Some((ours, capnp))
1288 } else {
1289 None
1290 }
1291}
1292
1293fn codec_size_caption(s: &CodecScenario) -> Option<String> {
1295 codec_best_vs_competitor(s).map(|(ours, rival, rsize)| {
1296 format!(
1297 "LOGOS {} vs best of the rest ({rival}) {} — {:.2}× smaller",
1298 format_bytes(ours as f64),
1299 format_bytes(rsize as f64),
1300 rsize as f64 / ours.max(1) as f64
1301 )
1302 })
1303}
1304
1305fn codec_ra_caption(s: &CodecScenario) -> Option<String> {
1307 codec_random_access_vs_capnp(s).map(|(ours, capnp)| {
1308 format!(
1309 "LOGOS {} vs Cap'n Proto {} — {:.2}× faster open + read",
1310 format_ns(ours),
1311 format_ns(capnp),
1312 capnp / ours.max(1.0)
1313 )
1314 })
1315}
1316
1317fn codec_axis_wins(d: &CodecData, axis: CodecAxis) -> (usize, usize) {
1320 let v = |r: &CodecRow| -> Option<f64> {
1321 match axis {
1322 CodecAxis::Size => Some(r.size as f64),
1323 CodecAxis::Encode => Some(r.enc_ns),
1324 CodecAxis::Decode => Some(r.dec_ns),
1325 CodecAxis::ReadOne => r.read_one_ns,
1326 }
1327 };
1328 let fair: Vec<&CodecScenario> = d.scenarios.iter().filter(|s| s.kind == "fair").collect();
1329 let wins = fair
1330 .iter()
1331 .filter(|s| {
1332 let lo = s.rows.iter().filter(|r| is_logos_codec(&r.codec)).filter_map(&v).fold(f64::INFINITY, f64::min);
1333 let co = s.rows.iter().filter(|r| !is_logos_codec(&r.codec)).filter_map(&v).fold(f64::INFINITY, f64::min);
1334 lo.is_finite() && co.is_finite() && lo <= co
1335 })
1336 .count();
1337 (wins, fair.len())
1338}
1339
1340fn codec_top_showcase(d: &CodecData) -> Option<f64> {
1342 d.scenarios
1343 .iter()
1344 .filter(|s| s.kind == "showcase")
1345 .filter_map(|s| codec_best_vs_competitor(s).map(|(ours, _, rival)| rival as f64 / ours.max(1) as f64))
1346 .fold(None, |acc, r| Some(acc.map_or(r, |m: f64| m.max(r))))
1347}
1348
1349fn codec_geomean_size(d: &CodecData) -> f64 {
1351 let ratios: Vec<f64> = d
1352 .scenarios
1353 .iter()
1354 .filter(|s| s.kind == "fair")
1355 .filter_map(|s| codec_best_vs_competitor(s).map(|(ours, _, rival)| rival as f64 / ours.max(1) as f64))
1356 .collect();
1357 if ratios.is_empty() {
1358 return 1.0;
1359 }
1360 (ratios.iter().map(|r| r.ln()).sum::<f64>() / ratios.len() as f64).exp()
1361}
1362
1363fn codec_best_row(s: &CodecScenario) -> Option<&CodecRow> {
1366 s.rows.iter().find(|r| r.codec == "logos (BEST: all knobs)")
1367}
1368
1369fn codec_good_for(id: &str) -> Option<&'static str> {
1373 Some(match id {
1374 "ints" => "ID columns, counters, quantities, foreign keys",
1375 "floats" => "measurements, prices, scientific readings",
1376 "timeseries" => "sensor / financial streams that vary slowly — xor-delta shines",
1377 "points" => "coordinates, vectors, spatial data",
1378 "records" => "row / struct lists — API payloads, database rows, events",
1379 "strings" => "text-heavy payloads — names, labels, log lines",
1380 "bools" => "flag columns, feature toggles, bitsets",
1381 "adv_negative" => "signed data that trips formats without zig-zag",
1382 "adv_repetitive" => "columns with long runs — status codes, repeated defaults",
1383 "adv_huge" => "wide 64-bit values — hashes, ids, nanosecond timestamps",
1384 "random_access" => "reading ONE field of a big message without decoding the rest",
1385 "affine_showcase" => "arithmetic progressions — row ids, ramps, fixed offsets",
1386 "poly_showcase" => "polynomial columns — accumulators, quadratic ramps",
1387 "categorical" => "low-cardinality labels — enums, tags, country codes",
1388 "int_set" => "membership sets — id sets, allow-lists",
1389 "int_map" => "int-keyed maps — lookup tables, adjacency lists",
1390 _ => return None,
1391 })
1392}
1393
1394fn codec_speed_pick(s: &CodecScenario) -> Option<(String, usize, f64)> {
1398 let best = codec_best_row(s)?;
1399 let pick = s
1400 .rows
1401 .iter()
1402 .filter(|r| is_logos_codec(&r.codec) && r.codec != "logos (BEST: all knobs)" && r.dec_ns > 0.0)
1403 .min_by(|a, b| a.dec_ns.partial_cmp(&b.dec_ns).unwrap_or(std::cmp::Ordering::Equal))?;
1404 (pick.dec_ns < best.dec_ns * 0.9).then(|| (codec_label(&pick.codec).to_string(), pick.size, pick.dec_ns))
1405}
1406
1407fn codec_fair_fight(s: &CodecScenario) -> Option<(usize, usize, String)> {
1412 let logos = codec_best_row(s)?.size; let rival = s
1414 .rows
1415 .iter()
1416 .filter(|r| !is_logos_codec(&r.codec))
1417 .filter_map(|r| r.fair_size.map(|f| (f, r.codec.clone())))
1418 .min_by_key(|(f, _)| *f)?;
1419 Some((logos, rival.0, codec_label(&rival.1).to_string()))
1420}
1421
1422fn codec_fair_size_wins(d: &CodecData) -> (usize, usize) {
1425 let fair: Vec<&CodecScenario> = d.scenarios.iter().filter(|s| s.kind == "fair").collect();
1426 let wins = fair.iter().filter(|s| matches!(codec_fair_fight(s), Some((lo, co, _)) if lo <= co)).count();
1427 (wins, fair.len())
1428}
1429
1430fn codec_winner_card(s: &CodecScenario) -> Element {
1434 let Some(best) = codec_best_row(s) else { return rsx! {} };
1435 let Some(chosen) = best.chosen.as_ref() else { return rsx! {} };
1436 let headline = match codec_best_vs_competitor(s) {
1437 Some((ours, rival, rsize)) => format!(
1438 "\u{2714} LOGOS ships {} \u{2014} {:.2}\u{00d7} smaller than {}",
1439 format_bytes(ours as f64),
1440 rsize as f64 / ours.max(1) as f64,
1441 rival
1442 ),
1443 None => format!("\u{2714} LOGOS ships {}", format_bytes(best.size as f64)),
1444 };
1445 let config_line = format!("won with: {}", chosen.summary);
1446 let speed_line = format!("encode {} \u{00b7} decode {}", format_ns(best.enc_ns), format_ns(best.dec_ns));
1447 let good = codec_good_for(&s.id);
1448 let fair = codec_fair_fight(s).map(|(lo, co, rlabel)| {
1451 let verdict = if lo < co { "still smaller" } else if lo == co { "tie" } else { "rival smaller" };
1452 format!(
1453 "Fair fight (all compressed): LOGOS {} vs {} {} \u{2014} {}",
1454 format_bytes(lo as f64),
1455 rlabel,
1456 format_bytes(co as f64),
1457 verdict
1458 )
1459 });
1460 let speed_pick = codec_speed_pick(s).map(|(label, size, dec)| {
1461 format!("Prioritize speed? {} \u{2014} {} \u{00b7} decode {}", label, format_bytes(size as f64), format_ns(dec))
1462 });
1463 rsx! {
1464 div {
1465 style: "background: linear-gradient(135deg, rgba(34,211,238,0.10), rgba(167,139,250,0.08)); border: 1px solid rgba(34,211,238,0.28); border-radius: 12px; padding: 12px 14px; margin: 6px 0 12px;",
1466 div { style: "font-size: 13px; font-weight: 700; color: #67e8f9;", "{headline}" }
1467 div { style: "font-size: 12px; color: rgba(229,231,235,0.85); margin-top: 4px;", "{config_line}" }
1468 div { style: "font-size: 12px; color: rgba(229,231,235,0.6); margin-top: 2px;", "{speed_line}" }
1469 if let Some(g) = good {
1470 div { style: "font-size: 12px; color: rgba(167,139,250,0.9); margin-top: 4px;", "Good for: {g}" }
1471 }
1472 if let Some(f) = fair {
1473 div { style: "font-size: 11px; color: rgba(229,231,235,0.55); margin-top: 4px;", "{f}" }
1474 }
1475 if let Some(sp) = speed_pick {
1476 div { style: "font-size: 11px; color: rgba(229,231,235,0.5); margin-top: 5px; border-top: 1px solid rgba(255,255,255,0.06); padding-top: 5px;", "{sp}" }
1477 }
1478 }
1479 }
1480}
1481
1482fn codec_dial_glossary() -> Element {
1487 let benched: &[(&str, &str)] = &[
1488 ("varint", "LEB128 — smallest bytes; the default for a bandwidth-bound network link"),
1489 ("fixed (memcpy)", "raw 8-byte i64 — fastest decode on a datacenter / RDMA link, ~4\u{00d7} the size"),
1490 ("group-varint", "varint-class size, SIMD-decoded several ints at once — small AND fast"),
1491 ("auto structure", "per-column bake-off (delta \u{00b7} delta-of-delta \u{00b7} FOR bit-pack \u{00b7} run-length \u{00b7} dictionary \u{00b7} affine \u{00b7} polynomial) — ships the smallest, never larger than varint"),
1492 ("xor-delta floats", "Gorilla-style — slowly-varying float streams shrink; high-entropy data falls back to memcpy"),
1493 ("struct-view", "an offset table per field so any ONE field reads in O(1) — Cap'n Proto's game; larger on the wire"),
1494 ("compression", "deflate / lz4 / zstd over the whole frame, kept only when smaller — for bulky or redundant payloads"),
1495 ];
1496 let also: &[(&str, &str)] = &[
1497 ("checksum", "an FNV tag so the receiver rejects a corrupted frame (Raw \u{2194} Checked)"),
1498 ("type-id", "elide struct / enum names on a Logos\u{2194}Logos link once both peers know the schema"),
1499 ("FEC", "Reed-Solomon shards — reconstruct the message from any K of N (lossy links, multicast)"),
1500 ("dedup", "a subtree reached by the same reference ships once + backrefs; the decoder rebuilds the sharing"),
1501 ];
1502 let item = |name: &'static str, desc: &'static str| {
1503 rsx! {
1504 div { style: "margin: 3px 0;",
1505 span { style: "color: #67e8f9; font-weight: 600;", "{name}" }
1506 span { style: "color: rgba(229,231,235,0.6);", " \u{2014} {desc}" }
1507 }
1508 }
1509 };
1510 rsx! {
1511 div {
1512 style: "background: rgba(255,255,255,0.02); border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; padding: 14px 16px; margin: 6px 0 18px; font-size: 12px; line-height: 1.5;",
1513 div { style: "font-size: 13px; font-weight: 700; color: #fff; margin-bottom: 4px;", "The dials \u{2014} every form is self-describing, so the decoder needs no hint" }
1514 div { style: "color: rgba(229,231,235,0.5); margin-bottom: 6px;", "In these benchmarks:" }
1515 for (name, desc) in benched.iter().copied() { {item(name, desc)} }
1516 div { style: "color: rgba(229,231,235,0.5); margin: 10px 0 6px;", "Also available (not exercised by the charts below):" }
1517 for (name, desc) in also.iter().copied() { {item(name, desc)} }
1518 }
1519 }
1520}
1521
1522#[component]
1528pub fn CodecSection() -> Element {
1529 let d = codec_data().expect("rendered only inside the loaded Benchmarks page");
1530 let (wins, total) = codec_size_wins(d);
1531 let fair: Vec<&CodecScenario> = d.scenarios.iter().filter(|s| s.kind == "fair").collect();
1532 let adversarial: Vec<&CodecScenario> = d.scenarios.iter().filter(|s| s.kind == "adversarial").collect();
1533 let random: Vec<&CodecScenario> = d.scenarios.iter().filter(|s| s.kind == "random_access").collect();
1534 let structural: Vec<&CodecScenario> = d.scenarios.iter().filter(|s| s.kind == "structural").collect();
1535 let showcase: Vec<&CodecScenario> = d.scenarios.iter().filter(|s| s.kind == "showcase").collect();
1536
1537 let mut comp_names = vec!["bincode", "postcard", "MessagePack", "CBOR", "JSON"];
1538 if d.metadata.features.iter().any(|f| f == "arrow-bench") {
1539 comp_names.push("Apache Arrow");
1540 }
1541 if d.metadata.features.iter().any(|f| f == "protobuf") {
1542 comp_names.push("Protobuf/gRPC");
1543 }
1544 if d.metadata.features.iter().any(|f| f == "capnproto") {
1545 comp_names.push("Cap'n Proto");
1546 }
1547 let comp_list = comp_names.join(", ");
1548
1549 let hint = "font-size: 12px; color: rgba(229,231,235,0.55); margin: 10px 0 4px;";
1550 let title = "font-size: 15px; font-weight: 700; color: #fff;";
1551 let cap = "font-size: 12px; color: rgba(34,211,238,0.85); margin: 2px 0 6px;";
1552
1553 rsx! {
1554 div { class: "bench-section", id: "serialization",
1555 div { class: "bench-section-title", "Serialization \u{2014} the wire codec" }
1556 div { class: "bench-section-desc",
1557 "Speed of code is one axis; bytes on the wire are another. The LOGOS wire codec is benched head-to-head against {comp_list} \u{2014} the same logical data through every codec, on the same machine, with seeded-random payloads (never a tidy 0..n sequence that would hand a structural codec a free win). LOGOS wins encode AND decode on every fair workload and is competitive-to-smaller on size; the honest details are on each card."
1558 }
1559
1560 { codec_dial_glossary() }
1562
1563 {
1565 let (enc_wins, _) = codec_axis_wins(d, CodecAxis::Encode);
1566 let (dec_wins, _) = codec_axis_wins(d, CodecAxis::Decode);
1567 let (fair_wins, fair_total) = codec_fair_size_wins(d);
1568 let geo = format!("{:.1}\u{00d7}", codec_geomean_size(d));
1569 let ra = random.first().and_then(|s| codec_random_access_vs_capnp(s));
1570 let ra_str = ra.map(|(o, c)| format!("{:.1}\u{00d7}", c / o.max(1.0)));
1571 let top_str = codec_top_showcase(d).map(|r| format!("{:.0}\u{00d7}", r));
1572 rsx! {
1573 div { class: "bench-summary",
1574 div { class: "bench-summary-card",
1575 div { class: "bench-summary-value green", "{enc_wins}/{total}" }
1576 div { class: "bench-summary-label", "fastest encode (fair workloads)" }
1577 }
1578 div { class: "bench-summary-card",
1579 div { class: "bench-summary-value green", "{dec_wins}/{total}" }
1580 div { class: "bench-summary-label", "fastest decode (fair workloads)" }
1581 }
1582 div { class: "bench-summary-card",
1583 div { class: "bench-summary-value cyan", "{wins}/{total}" }
1584 div { class: "bench-summary-label", "smallest out-of-the-box (we auto-compress; rivals raw)" }
1585 }
1586 div { class: "bench-summary-card",
1587 div { class: "bench-summary-value cyan", "{fair_wins}/{fair_total}" }
1588 div { class: "bench-summary-label", "smallest in a fair fight (everyone compressed)" }
1589 }
1590 if let Some(ra_str) = ra_str {
1591 div { class: "bench-summary-card",
1592 div { class: "bench-summary-value cyan", "{ra_str}" }
1593 div { class: "bench-summary-label", "faster random-access read vs Cap'n Proto" }
1594 }
1595 }
1596 if let Some(top_str) = top_str {
1597 div { class: "bench-summary-card",
1598 div { class: "bench-summary-value purple", "up to {top_str}" }
1599 div { class: "bench-summary-label", "structural showcases (smaller)" }
1600 }
1601 }
1602 div { class: "bench-summary-card",
1603 div { class: "bench-summary-value cyan", "{geo}" }
1604 div { class: "bench-summary-label", "geo-mean smaller, out of the box" }
1605 }
1606 }
1607 div {
1608 style: "background: linear-gradient(135deg, rgba(34,211,238,0.10), rgba(167,139,250,0.10)); border: 1px solid rgba(34,211,238,0.25); border-radius: 12px; padding: 16px 18px; margin: 4px 0 20px;",
1609 div { style: "font-size: 13px; color: rgba(229,231,235,0.7); line-height: 1.5;",
1610 "Every figure is measured on the same logical data, same machine, seeded-random. LOGOS wins encode AND decode on every fair workload \u{2014} the default dial is a pure memcpy that never searches at send time (you pick the shape ahead of time), so it beats even bincode's raw copy; decode is faster because the format is columnar and typed. On SIZE we\u{2019}re honest two ways: \u{201c}out of the box\u{201d} LOGOS auto-compresses and rivals ship raw (a real transport advantage), and the \u{201c}fair fight\u{201d} gives every rival the SAME compression \u{2014} there LOGOS wins on structured data and ties on random primitive arrays (a memcpy is a memcpy). The all-knobs winner\u{2019}s exact dials, and both size comparisons, are on each card. Decode is timed \u{201c}to usable\u{201d} (every value touched). The contrived generator showcases (a pure 0,1,2\u{2026} ramp) are fenced off from the fair results."
1611 }
1612 }
1613 }
1614 }
1615
1616 for s in fair.iter() {
1618 div { style: "margin-bottom: 26px;",
1619 div { style: "{title}", "{s.title} \u{00b7} n = {s.n}" }
1620 { codec_winner_card(s) }
1621 if let Some(c) = codec_size_caption(s) {
1622 div { style: "{cap}", "{c}" }
1623 }
1624 div { style: "{hint}", "Wire size \u{2014} smaller is better, bars proportional" }
1625 { codec_axis_chart(s, CodecAxis::Size) }
1626 div { style: "{hint}", "Encode (ns/op) \u{2014} smaller is better, bars proportional" }
1627 { codec_axis_chart(s, CodecAxis::Encode) }
1628 div { style: "{hint}", "Decode to usable (ns/op) \u{2014} smaller is better, bars proportional" }
1629 { codec_axis_chart(s, CodecAxis::Decode) }
1630 }
1631 }
1632
1633 for s in random.iter() {
1635 div { style: "margin-bottom: 26px;",
1636 div { style: "{title}", "{s.title} \u{00b7} n = {s.n}" }
1637 div { style: "font-size: 12px; color: rgba(229,231,235,0.6); margin: 4px 0 6px; line-height: 1.5;",
1638 "A message arrives; you open it and read ONE field. This is exactly what Cap'n Proto is built for. LOGOS opens its record-list view (row + field offset tables) in O(1); the self-describing codecs must decode the whole message to index it."
1639 }
1640 if let Some(c) = codec_ra_caption(s) {
1641 div { style: "{cap}", "{c}" }
1642 }
1643 div { style: "{hint}", "Open + read one field (ns/op) \u{2014} smaller is better, bars proportional" }
1644 { codec_axis_chart(s, CodecAxis::ReadOne) }
1645 }
1646 }
1647
1648 if !adversarial.is_empty() {
1650 div { style: "font-size: 15px; font-weight: 700; color: #fff; margin-top: 28px;", "Adversarial shapes" }
1651 div { style: "font-size: 12px; color: rgba(229,231,235,0.55); margin: 4px 0 12px; line-height: 1.5;",
1652 "The data shapes that wreck fixed-format codecs \u{2014} all dials on."
1653 }
1654 for s in adversarial.iter() {
1655 div { style: "margin-bottom: 20px;",
1656 div { style: "{title}", "{s.title} \u{00b7} n = {s.n}" }
1657 { codec_winner_card(s) }
1658 if let Some(c) = codec_size_caption(s) {
1659 div { style: "{cap}", "{c}" }
1660 }
1661 div { style: "{hint}", "Wire size \u{2014} smaller is better, bars proportional" }
1662 { codec_axis_chart(s, CodecAxis::Size) }
1663 }
1664 }
1665 }
1666
1667 if !structural.is_empty() {
1669 div { style: "font-size: 15px; font-weight: 700; color: #fff; margin-top: 28px;", "Structural wins on real data" }
1670 div { style: "font-size: 12px; color: rgba(229,231,235,0.6); margin: 4px 0 12px; line-height: 1.5;",
1671 "Not math tricks \u{2014} the standard columnar techniques (dictionary encoding for low-cardinality labels, bit-packing for booleans) that Parquet and Arrow are built on, applied automatically. Common shapes in real payloads, and LOGOS ships them by default when they win."
1672 }
1673 for s in structural.iter() {
1674 div { style: "margin-bottom: 22px;",
1675 div { style: "{title}", "{s.title} \u{00b7} n = {s.n}" }
1676 { codec_winner_card(s) }
1677 if let Some(c) = codec_size_caption(s) {
1678 div { style: "{cap}", "{c}" }
1679 }
1680 div { style: "{hint}", "Wire size \u{2014} smaller is better, bars proportional" }
1681 { codec_axis_chart(s, CodecAxis::Size) }
1682 }
1683 }
1684 }
1685
1686 if !showcase.is_empty() {
1688 div {
1689 style: "background: linear-gradient(135deg, rgba(167,139,250,0.10), rgba(34,211,238,0.06)); border: 1px solid rgba(167,139,250,0.25); border-radius: 12px; padding: 14px 16px; margin: 18px 0 12px;",
1690 div { style: "font-size: 13px; color: rgba(229,231,235,0.8); line-height: 1.5;",
1691 "\u{26a1} Showcases, kept separate from the fair results: contrived columns where the codec ships the GENERATING RULE instead of the data \u{2014} an affine progression as (base, stride, n), a polynomial as its finite-difference seeds, a consecutive set as base+stride+count \u{2014} winning by orders of magnitude. Not general-data results; they show the ceiling when structure is perfect."
1692 }
1693 }
1694 for s in showcase.iter() {
1695 div { style: "margin-bottom: 22px;",
1696 div { style: "{title}", "{s.title} \u{00b7} n = {s.n}" }
1697 { codec_winner_card(s) }
1698 if let Some(c) = codec_size_caption(s) {
1699 div { style: "{cap}", "{c}" }
1700 }
1701 div { style: "{hint}", "Wire size \u{2014} smaller is better, bars proportional" }
1702 { codec_axis_chart(s, CodecAxis::Size) }
1703 }
1704 }
1705 }
1706
1707 div { style: "font-size: 11px; color: rgba(229,231,235,0.4); line-height: 1.5; border-top: 1px solid rgba(255,255,255,0.08); padding-top: 12px;",
1708 "Method: each value space (int/float arrays, point & record lists, strings) is encoded by every codec from the same seeded-random data; size is exact bytes (no envelope), encode/decode are ns per whole-message op, taken with a warm-up. Cap'n Proto is read zero-copy (its best). Bars are linear within each chart — every length is its exact fraction of the slowest, so the gap you see is the true ratio. Measured on {d.metadata.cpu}, {d.metadata.os}."
1709 }
1710 }
1711 }
1712}
1713
1714#[derive(Deserialize)]
1718struct InterpData {
1719 #[serde(default)]
1720 metadata: InterpMetadata,
1721 #[serde(default)]
1722 benchmarks: Vec<InterpBenchmark>,
1723 #[serde(default)]
1724 summary: InterpSummary,
1725 #[serde(default)]
1726 startup: InterpStartup,
1727 #[serde(default)]
1730 interpreter_sizes: Option<InterpreterSizes>,
1731}
1732
1733#[derive(Deserialize, Clone)]
1734struct InterpreterSizes {
1735 #[allow(dead_code)]
1736 method: String,
1737 engines: HashMap<String, BinSizes>,
1739 #[serde(default)]
1742 wasm_bundle_bytes: Option<f64>,
1743}
1744
1745#[derive(Deserialize, Default)]
1746struct InterpStartup {
1747 #[serde(default)]
1748 runs: u32,
1749 #[serde(default)]
1750 engines: HashMap<String, StartupTiming>,
1751}
1752
1753#[derive(Deserialize, Default, Clone)]
1754struct StartupTiming {
1755 #[serde(default)]
1756 mean_ms: f64,
1757 #[serde(default)]
1758 min_ms: f64,
1759 #[serde(default)]
1760 median_ms: f64,
1761}
1762
1763#[derive(Deserialize, Default)]
1764struct InterpMetadata {
1765 #[serde(default)]
1766 node: String,
1767 #[serde(default)]
1768 date: String,
1769}
1770
1771#[derive(Deserialize, Default)]
1772struct InterpSummary {
1773 #[serde(default)]
1774 geometric_mean_logos_interp_over_node: f64,
1775 #[serde(default)]
1778 geometric_mean_logos_tiered_over_node: f64,
1779 #[serde(default)]
1782 geometric_mean_logos_aot_over_node: f64,
1783}
1784
1785#[derive(Deserialize)]
1786struct InterpBenchmark {
1787 id: String,
1788 name: String,
1789 #[serde(default)]
1790 reference_size: String,
1791 #[serde(default)]
1792 interpreter_engine: String,
1793 #[serde(default)]
1794 scaling: HashMap<String, HashMap<String, TimingResult>>,
1795}
1796
1797#[cfg(not(target_arch = "wasm32"))]
1798static INTERP_DATA: LazyLock<InterpData> = LazyLock::new(|| {
1799 serde_json::from_str(include_str!("../../../../../benchmarks/results/latest-interp.json")).unwrap()
1800});
1801#[cfg(target_arch = "wasm32")]
1802static INTERP_DATA: OnceLock<InterpData> = OnceLock::new();
1803
1804fn interp_data() -> Option<&'static InterpData> {
1806 #[cfg(not(target_arch = "wasm32"))]
1807 {
1808 Some(&INTERP_DATA)
1809 }
1810 #[cfg(target_arch = "wasm32")]
1811 {
1812 INTERP_DATA.get()
1813 }
1814}
1815
1816struct BenchSources {
1817 c: &'static str,
1818 cpp: &'static str,
1819 rust: &'static str,
1820 zig: &'static str,
1821 go: &'static str,
1822 java: &'static str,
1823 js: &'static str,
1824 python: &'static str,
1825 ruby: &'static str,
1826 nim: &'static str,
1827 logos: &'static str,
1828}
1829
1830#[cfg(not(target_arch = "wasm32"))]
1831macro_rules! bench_sources {
1832 ($name:literal) => {
1833 ($name, BenchSources {
1834 c: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/main.c")),
1835 cpp: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/main.cpp")),
1836 rust: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/main.rs")),
1837 zig: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/main.zig")),
1838 go: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/main.go")),
1839 java: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/Main.java")),
1840 js: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/main.js")),
1841 python: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/main.py")),
1842 ruby: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/main.rb")),
1843 nim: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/main.nim")),
1844 logos: include_str!(concat!("../../../../../benchmarks/programs/", $name, "/main.lg")),
1845 })
1846 };
1847}
1848
1849#[cfg(not(target_arch = "wasm32"))]
1850static SOURCES: LazyLock<[(&'static str, BenchSources); 32]> = LazyLock::new(|| [
1851 bench_sources!("fib"),
1853 bench_sources!("ackermann"),
1854 bench_sources!("nqueens"),
1855 bench_sources!("bubble_sort"),
1857 bench_sources!("mergesort"),
1858 bench_sources!("quicksort"),
1859 bench_sources!("counting_sort"),
1860 bench_sources!("heap_sort"),
1861 bench_sources!("nbody"),
1863 bench_sources!("mandelbrot"),
1864 bench_sources!("spectral_norm"),
1865 bench_sources!("pi_leibniz"),
1866 bench_sources!("gcd"),
1868 bench_sources!("collatz"),
1869 bench_sources!("primes"),
1870 bench_sources!("sieve"),
1872 bench_sources!("matrix_mult"),
1873 bench_sources!("prefix_sum"),
1874 bench_sources!("array_reverse"),
1875 bench_sources!("array_fill"),
1876 bench_sources!("collect"),
1878 bench_sources!("two_sum"),
1879 bench_sources!("histogram"),
1880 bench_sources!("knapsack"),
1882 bench_sources!("coins"),
1883 bench_sources!("fannkuch"),
1885 bench_sources!("strings"),
1887 bench_sources!("binary_trees"),
1888 bench_sources!("loop_sum"),
1890 bench_sources!("fib_iterative"),
1891 bench_sources!("graph_bfs"),
1892 bench_sources!("string_search"),
1893]);
1894#[cfg(target_arch = "wasm32")]
1895static SOURCES: OnceLock<HashMap<String, BenchSources>> = OnceLock::new();
1896
1897fn bench_sources_for(id: &str) -> Option<&'static BenchSources> {
1900 #[cfg(not(target_arch = "wasm32"))]
1901 {
1902 SOURCES.iter().find(|(name, _)| *name == id).map(|(_, s)| s)
1903 }
1904 #[cfg(target_arch = "wasm32")]
1905 {
1906 SOURCES.get()?.get(id)
1907 }
1908}
1909
1910#[cfg(target_arch = "wasm32")]
1913#[derive(Deserialize)]
1914struct BenchSourcesOwned {
1915 c: String,
1916 cpp: String,
1917 rust: String,
1918 zig: String,
1919 go: String,
1920 java: String,
1921 js: String,
1922 python: String,
1923 ruby: String,
1924 nim: String,
1925 logos: String,
1926}
1927
1928#[cfg(target_arch = "wasm32")]
1929impl BenchSourcesOwned {
1930 fn pin(self) -> BenchSources {
1931 fn pin(s: String) -> &'static str {
1932 Box::leak(s.into_boxed_str())
1933 }
1934 BenchSources {
1935 c: pin(self.c),
1936 cpp: pin(self.cpp),
1937 rust: pin(self.rust),
1938 zig: pin(self.zig),
1939 go: pin(self.go),
1940 java: pin(self.java),
1941 js: pin(self.js),
1942 python: pin(self.python),
1943 ruby: pin(self.ruby),
1944 nim: pin(self.nim),
1945 logos: pin(self.logos),
1946 }
1947 }
1948}
1949
1950#[cfg(target_arch = "wasm32")]
1953async fn load_bench_bundle() -> Result<(), String> {
1954 use crate::ui::data_fetch::fetch_static_json;
1955 let bench: BenchmarkData = fetch_static_json("/data/latest.json").await?;
1956 let solver: SolverData = fetch_static_json("/data/solvers.json").await?;
1957 let codec: CodecData = fetch_static_json("/data/latest-codec.json").await?;
1958 let interp: InterpData = fetch_static_json("/data/latest-interp.json").await?;
1959 let sources: HashMap<String, BenchSourcesOwned> =
1960 fetch_static_json("/data/bench-sources.json").await?;
1961 let _ = BENCH_DATA.set(bench);
1962 let _ = SOLVER_DATA.set(solver);
1963 let _ = CODEC_DATA.set(codec);
1964 let _ = INTERP_DATA.set(interp);
1965 let _ = SOURCES.set(sources.into_iter().map(|(id, s)| (id, s.pin())).collect());
1966 Ok(())
1967}
1968
1969const GITHUB_REPO: &str = "https://github.com/Brahmastra-Labs/logicaffeine";
1970
1971fn format_time(ms: f64) -> String {
1972 if ms >= 1000.0 {
1973 format!("{:.2}s", ms / 1000.0)
1974 } else if ms >= 1.0 {
1975 format!("{:.1}ms", ms)
1976 } else {
1977 format!("{:.0}\u{00b5}s", ms * 1000.0)
1978 }
1979}
1980
1981fn format_timeout(timeout_ms: f64) -> String {
1982 let mins = timeout_ms / 60_000.0;
1983 if mins >= 1.0 {
1984 format!(">{:.0}min", mins)
1985 } else {
1986 format!(">{:.0}s", timeout_ms / 1000.0)
1987 }
1988}
1989
1990fn tier_label(tier: &str) -> &'static str {
1991 match tier {
1992 "systems" => "Systems",
1993 "managed" => "Managed",
1994 "interpreted" => "Interpreted",
1995 "transpiled" => "Transpiled",
1996 _ => "Other",
1997 }
1998}
1999
2000fn lang_color(lang_id: &str) -> &'static str {
2001 match lang_id {
2002 "c" => "#555555",
2003 "cpp" => "#f34b7d",
2004 "rust" => "#dea584",
2005 "zig" => "#f7a41d",
2006 "logos_release" => "#00d4ff",
2007 "go" => "#3fb950",
2008 "java" => "#b07219",
2009 "js" => "#f7df1e",
2010 "logos_interp" => "#ff8c00",
2011 "logos_tiered" => "#ffb84d",
2012 "logos_aot" => "#00d4ff",
2013 "python" => "#3776ab",
2014 "ruby" => "#cc342d",
2015 "nim" => "#ffe953",
2016 _ => "#94a3b8",
2017 }
2018}
2019
2020fn lang_label(lang_id: &str) -> &'static str {
2021 match lang_id {
2022 "c" => "C",
2023 "cpp" => "C++",
2024 "rust" => "Rust",
2025 "zig" => "Zig",
2026 "logos_release" => "LOGOS",
2027 "go" => "Go",
2028 "java" => "Java",
2029 "js" => "JavaScript",
2030 "logos_interp" => "LOGOS (eager)",
2031 "logos_tiered" => "LOGOS (tiered)",
2032 "logos_aot" => "LOGOS (AOT-native)",
2033 "python" => "Python",
2034 "ruby" => "Ruby",
2035 "nim" => "Nim",
2036 _ => "Other",
2037 }
2038}
2039
2040fn lang_ext(lang_id: &str) -> &'static str {
2041 match lang_id {
2042 "c" => "main.c",
2043 "cpp" => "main.cpp",
2044 "rust" => "main.rs",
2045 "zig" => "main.zig",
2046 "go" => "main.go",
2047 "java" => "Main.java",
2048 "js" => "main.js",
2049 "python" => "main.py",
2050 "ruby" => "main.rb",
2051 "nim" => "main.nim",
2052 _ => "main",
2053 }
2054}
2055
2056fn compiler_label(key: &str) -> &'static str {
2057 match key {
2058 "gcc_-o3" => "gcc -O3 -march=native -flto",
2059 "g++_-o3" => "g++ -O3 -march=native -flto",
2060 "rustc_-o3" => "rustc -O3 -C lto=fat -C target-cpu=native",
2061 "gcc_-o2" => "gcc -O2",
2063 "g++_-o2" => "g++ -O2",
2064 "rustc_-o" => "rustc -O",
2065 "go_build" => "go build (release)",
2066 "javac" => "javac",
2067 "nim_c" => "nim c -d:release -march=native",
2068 "zig_build-exe" => "zig build-exe -O ReleaseFast -mcpu native",
2069 "largo_build" => "largo build (debug)",
2070 "largo_build_--release" => "largo build --release \u{2192} rustc -O3 -C lto=fat -C codegen-units=1 -C target-cpu=native",
2071 _ => "unknown",
2072 }
2073}
2074
2075fn get_source(sources: &BenchSources, lang_id: &str) -> &'static str {
2076 match lang_id {
2077 "c" => sources.c,
2078 "cpp" => sources.cpp,
2079 "rust" => sources.rust,
2080 "zig" => sources.zig,
2081 "go" => sources.go,
2082 "java" => sources.java,
2083 "js" => sources.js,
2084 "python" => sources.python,
2085 "ruby" => sources.ruby,
2086 "nim" => sources.nim,
2087 _ => "",
2088 }
2089}
2090
2091fn collapse_note(id: &str) -> Option<&'static str> {
2099 match id {
2100 "fib" => Some("This one collapsed. The LOGOS optimizer folds the naive recursion to a closed form, so it does far less work than the runtime recursion the other languages execute — a compiler transform, not like-for-like codegen. See the generated Rust below."),
2101 "ackermann" => Some("This one collapsed. Deep recursion is folded by the optimizer instead of being executed call-by-call — a compiler transform, not like-for-like codegen. See the generated Rust below."),
2102 "binary_trees" => Some("This one collapsed. The allocate-and-checksum tree is reduced by the optimizer rather than built at runtime — a compiler transform, not like-for-like codegen. See the generated Rust below."),
2103 "loop_sum" => Some("This one collapsed. The accumulation loop is replaced with its closed-form sum (O(n) becomes O(1)) — a compiler transform, not like-for-like codegen. See the generated Rust below."),
2104 "collect" => Some("This one collapsed. The LOGOS optimizer folds the collection-building loop, doing far less work than inserting each element into a hash map at runtime — a compiler transform, not like-for-like codegen. See the generated Rust below."),
2105 _ => None,
2106 }
2107}
2108
2109fn effective_ref(b: &Benchmark) -> Option<&HashMap<String, TimingResult>> {
2112 if let Some(t) = b.scaling.get(b.reference_size.as_str()) {
2113 return Some(t);
2114 }
2115 b.sizes.iter().rev().find_map(|s| b.scaling.get(s))
2116}
2117
2118fn logos_apples_geomean(data: &BenchmarkData) -> f64 {
2121 let mut log_sum = 0.0_f64;
2122 let mut n = 0u32;
2123 for b in &data.benchmarks {
2124 if collapse_note(&b.id).is_some() {
2125 continue;
2126 }
2127 if let Some(t) = effective_ref(b) {
2128 if let (Some(c), Some(l)) = (t.get("c"), t.get("logos_release")) {
2129 if c.mean_ms > 0.0 && l.mean_ms > 0.0 {
2130 log_sum += (c.mean_ms / l.mean_ms).ln();
2131 n += 1;
2132 }
2133 }
2134 }
2135 }
2136 if n > 0 { (log_sum / n as f64).exp() } else { 0.0 }
2137}
2138
2139fn node_floored(t: &TimingResult) -> bool {
2143 t.mean_ms < 60.0
2144}
2145
2146fn interp_ref<'a>(b: &'a InterpBenchmark) -> Option<&'a HashMap<String, TimingResult>> {
2147 b.scaling.get(b.reference_size.as_str()).or_else(|| b.scaling.values().next())
2148}
2149
2150fn interp_speed_vs_v8(data: &InterpData) -> f64 {
2153 let mut log_sum = 0.0_f64;
2154 let mut n = 0u32;
2155 for b in &data.benchmarks {
2156 if let Some(t) = interp_ref(b) {
2157 if let (Some(js), Some(lg)) = (t.get("js"), t.get("logos_interp")) {
2158 if js.mean_ms > 0.0 && lg.mean_ms > 0.0 && !node_floored(js) {
2159 log_sum += (js.mean_ms / lg.mean_ms).ln();
2160 n += 1;
2161 }
2162 }
2163 }
2164 }
2165 if n > 0 { (log_sum / n as f64).exp() } else { 0.0 }
2166}
2167
2168fn aot_speed_vs_v8(data: &InterpData) -> f64 {
2173 let mut log_sum = 0.0_f64;
2174 let mut n = 0u32;
2175 for b in &data.benchmarks {
2176 if let Some(t) = interp_ref(b) {
2177 if let (Some(js), Some(lg)) = (t.get("js"), t.get("logos_aot")) {
2178 if js.mean_ms > 0.0 && lg.mean_ms > 0.0 && !node_floored(js) {
2179 log_sum += (js.mean_ms / lg.mean_ms).ln();
2180 n += 1;
2181 }
2182 }
2183 }
2184 }
2185 if n > 0 { (log_sum / n as f64).exp() } else { 0.0 }
2186}
2187
2188fn fmt_n(n: f64) -> String {
2189 if n >= 1.0e9 { format!("{:.0}B", n / 1.0e9) }
2190 else if n >= 1.0e6 { format!("{:.0}M", n / 1.0e6) }
2191 else if n >= 1.0e3 { format!("{:.0}k", n / 1.0e3) }
2192 else { format!("{:.0}", n) }
2193}
2194
2195fn scaling_chart(bench: &Benchmark, languages: &[Language]) -> Element {
2199 let mut sizes: Vec<(String, f64)> = bench.sizes.iter()
2200 .filter(|s| bench.scaling.contains_key(s.as_str()))
2201 .filter_map(|s| s.parse::<f64>().ok().map(|n| (s.clone(), n)))
2202 .collect();
2203 sizes.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
2204 if sizes.len() < 2 {
2205 return rsx! {};
2206 }
2207
2208 let mut t_min = f64::INFINITY;
2209 let mut t_max = 0.0_f64;
2210 for (s, _) in &sizes {
2211 if let Some(m) = bench.scaling.get(s) {
2212 for l in languages {
2213 if let Some(t) = m.get(&l.id) {
2214 if t.median_ms > 0.0 {
2215 t_min = t_min.min(t.median_ms);
2216 t_max = t_max.max(t.median_ms);
2217 }
2218 }
2219 }
2220 }
2221 }
2222 if !t_min.is_finite() || t_max <= 0.0 {
2223 return rsx! {};
2224 }
2225
2226 let nx_min = sizes.first().unwrap().1.ln();
2227 let nx_max = sizes.last().unwrap().1.ln();
2228 let x_span = (nx_max - nx_min).max(1e-9);
2229 let y_span = (t_max.ln() - t_min.ln()).max(1e-9);
2230
2231 let (x0, y0, pw, ph) = (54.0_f64, 14.0_f64, 572.0_f64, 220.0_f64);
2232 let px = |n: f64| x0 + (n.ln() - nx_min) / x_span * pw;
2233 let py = |t: f64| y0 + (1.0 - (t.ln() - t_min.ln()) / y_span) * ph;
2234
2235 let mut series: Vec<(String, String, Vec<(f64, f64)>)> = Vec::new();
2237 for l in languages {
2238 let mut coords: Vec<(f64, f64)> = Vec::new();
2239 for (s, n) in &sizes {
2240 if let Some(t) = bench.scaling.get(s).and_then(|m| m.get(&l.id)) {
2241 if t.median_ms > 0.0 {
2242 coords.push((px(*n), py(t.median_ms)));
2243 }
2244 }
2245 }
2246 if !coords.is_empty() {
2247 series.push((l.color.clone(), l.label.clone(), coords));
2248 }
2249 }
2250 if series.is_empty() {
2251 return rsx! {};
2252 }
2253
2254 let y_bottom = y0 + ph;
2255 let x_right = x0 + pw;
2256
2257 let mut svg_inner = String::new();
2261 svg_inner.push_str(&format!(
2262 "<line x1='{x0:.1}' y1='{y0:.1}' x2='{x0:.1}' y2='{y_bottom:.1}' stroke='rgba(255,255,255,0.15)' stroke-width='1'/>"
2263 ));
2264 svg_inner.push_str(&format!(
2265 "<line x1='{x0:.1}' y1='{y_bottom:.1}' x2='{x_right:.1}' y2='{y_bottom:.1}' stroke='rgba(255,255,255,0.15)' stroke-width='1'/>"
2266 ));
2267 svg_inner.push_str(&format!(
2268 "<text x='{:.1}' y='{:.1}' text-anchor='end' fill='rgba(229,231,235,0.45)' font-size='10'>{}</text>",
2269 x0 - 6.0, y0 + 4.0, format_time(t_max)
2270 ));
2271 svg_inner.push_str(&format!(
2272 "<text x='{:.1}' y='{:.1}' text-anchor='end' fill='rgba(229,231,235,0.45)' font-size='10'>{}</text>",
2273 x0 - 6.0, y_bottom, format_time(t_min)
2274 ));
2275 for (_, n) in &sizes {
2276 svg_inner.push_str(&format!(
2277 "<text x='{:.1}' y='{:.1}' text-anchor='middle' fill='rgba(229,231,235,0.45)' font-size='10'>n={}</text>",
2278 px(*n), y_bottom + 16.0, fmt_n(*n)
2279 ));
2280 }
2281 for (color, _label, coords) in &series {
2282 let pts = coords.iter().map(|(x, y)| format!("{x:.1},{y:.1}")).collect::<Vec<_>>().join(" ");
2283 svg_inner.push_str(&format!(
2284 "<polyline points='{pts}' fill='none' stroke='{color}' stroke-width='2' stroke-linejoin='round' stroke-linecap='round'/>"
2285 ));
2286 for (x, y) in coords {
2287 svg_inner.push_str(&format!("<circle cx='{x:.1}' cy='{y:.1}' r='2.5' fill='{color}'/>"));
2288 }
2289 }
2290
2291 rsx! {
2292 div { class: "bench-scaling",
2293 svg {
2294 view_box: "0 0 640 266",
2295 width: "100%",
2296 dangerous_inner_html: "{svg_inner}",
2297 }
2298 div { class: "bench-scaling-legend",
2299 for (color, label, _coords) in series.iter() {
2300 div { class: "bench-scaling-legend-item",
2301 span { class: "bench-scaling-legend-dot", style: "background: {color};" }
2302 "{label}"
2303 }
2304 }
2305 }
2306 }
2307 }
2308}
2309
2310fn format_mem(kb: f64) -> String {
2312 if kb >= 1024.0 * 1024.0 {
2313 format!("{:.1} GB", kb / 1024.0 / 1024.0)
2314 } else if kb >= 1024.0 {
2315 format!("{:.1} MB", kb / 1024.0)
2316 } else {
2317 format!("{kb:.0} KB")
2318 }
2319}
2320
2321fn format_bytes(bytes: f64) -> String {
2323 if bytes >= 1024.0 * 1024.0 * 1024.0 {
2324 format!("{:.1} GB", bytes / 1024.0 / 1024.0 / 1024.0)
2325 } else if bytes >= 1024.0 * 1024.0 {
2326 format!("{:.1} MB", bytes / 1024.0 / 1024.0)
2327 } else {
2328 format!("{:.0} KB", bytes / 1024.0)
2329 }
2330}
2331
2332fn footprint_label(bytes: f64, stripped: Option<f64>) -> String {
2336 let main = format_bytes(bytes);
2337 match stripped {
2338 Some(st) if st > 0.0 && st < bytes => {
2339 let s = format_bytes(st);
2340 if s != main { format!("{main} ({s} stripped)") } else { main }
2341 }
2342 _ => main,
2343 }
2344}
2345
2346fn shipped_bytes(as_built: f64, stripped: Option<f64>) -> f64 {
2352 match stripped {
2353 Some(s) if s > 0.0 && s <= as_built => s,
2354 _ => as_built,
2355 }
2356}
2357
2358fn shipped_footprint_label(as_built: f64, stripped: Option<f64>) -> String {
2363 let shipped = format_bytes(shipped_bytes(as_built, stripped));
2364 let built = format_bytes(as_built);
2365 format!("{shipped} ({built} as-built)")
2366}
2367
2368fn binary_size_bar_chart(bench: &Benchmark, languages: &[Language]) -> Element {
2374 let sizes = match &bench.binary_sizes {
2375 Some(s) => s,
2376 None => return rsx! {},
2377 };
2378 let mut bars: Vec<(String, String, f64, f64, Option<f64>, bool)> = languages.iter()
2380 .filter_map(|l| sizes.by_language.get(&l.id)
2381 .filter(|s| s.as_built > 0.0)
2382 .map(|s| (l.label.clone(), l.color.clone(), shipped_bytes(s.as_built, s.stripped), s.as_built, s.stripped, l.id == "logos_release")))
2383 .collect();
2384 if bars.is_empty() {
2385 return rsx! {};
2386 }
2387 bars.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(std::cmp::Ordering::Equal));
2388 let max = bars.iter().map(|b| b.2).fold(0.0_f64, f64::max).max(1.0);
2389
2390 rsx! {
2391 div { class: "bench-mem",
2392 div { class: "bench-chart-hint", "Shipped size of the compiled program \u{2014} the stripped, code-only binary you actually deploy, so every language is compared on the same basis. Shorter bar ships less; the as-built size (with symbols) is in parentheses." }
2393 div { class: "bench-chart",
2394 for (label, color, shipped, as_built, stripped, is_logos) in bars.iter() {
2395 {
2396 let pct = linear_bar_pct(*shipped, max);
2397 let s = shipped_footprint_label(*as_built, *stripped);
2398 let show_inside = pct > 32.0;
2399 let bar_class = if *is_logos { "bench-bar-fill logos-highlight" } else { "bench-bar-fill" };
2400 rsx! {
2401 div { class: "bench-bar-row",
2402 div { class: "bench-bar-label", "{label}" }
2403 div { class: "bench-bar-track",
2404 div { class: "{bar_class}", style: "width: {pct:.1}%; background: {color};",
2405 if show_inside {
2406 span { class: "bench-bar-time", "{s}" }
2407 }
2408 }
2409 }
2410 if !show_inside {
2411 span { class: "bench-bar-time-outside", "{s}" }
2412 }
2413 }
2414 }
2415 }
2416 }
2417 }
2418 }
2419 }
2420}
2421
2422fn memory_bar_chart(bench: &Benchmark, languages: &[Language]) -> Element {
2426 let mem = match &bench.memory {
2427 Some(m) => m,
2428 None => return rsx! {},
2429 };
2430 let size = if mem.by_size.contains_key(bench.reference_size.as_str()) {
2431 bench.reference_size.clone()
2432 } else {
2433 let mut ks: Vec<(String, f64)> = mem.by_size.keys()
2434 .filter_map(|s| s.parse::<f64>().ok().map(|n| (s.clone(), n)))
2435 .collect();
2436 ks.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
2437 match ks.last() {
2438 Some((s, _)) => s.clone(),
2439 None => return rsx! {},
2440 }
2441 };
2442 let row = match mem.by_size.get(&size) {
2443 Some(r) => r,
2444 None => return rsx! {},
2445 };
2446 let mut bars: Vec<(String, String, f64)> = languages.iter()
2447 .filter_map(|l| row.get(&l.id).and_then(|v| *v)
2448 .filter(|kb| *kb > 0.0)
2449 .map(|kb| (l.label.clone(), l.color.clone(), kb)))
2450 .collect();
2451 if bars.is_empty() {
2452 return rsx! {};
2453 }
2454 bars.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(std::cmp::Ordering::Equal));
2455 let max = bars.iter().map(|b| b.2).fold(0.0_f64, f64::max);
2456
2457 rsx! {
2458 div { class: "bench-mem",
2459 div { class: "bench-chart-hint", "Peak resident memory at n = {size} \u{2014} shorter bar uses less." }
2460 div { class: "bench-chart",
2461 for (label, color, kb) in bars.iter() {
2462 {
2463 let pct = (kb / max * 100.0).min(100.0);
2464 let s = format_mem(*kb);
2465 let show_inside = pct > 18.0;
2466 rsx! {
2467 div { class: "bench-bar-row",
2468 div { class: "bench-bar-label", "{label}" }
2469 div { class: "bench-bar-track",
2470 div { class: "bench-bar-fill", style: "width: {pct:.1}%; background: {color};",
2471 if show_inside {
2472 span { class: "bench-bar-time", "{s}" }
2473 }
2474 }
2475 }
2476 if !show_inside {
2477 span { class: "bench-bar-time-outside", "{s}" }
2478 }
2479 }
2480 }
2481 }
2482 }
2483 }
2484 }
2485 }
2486}
2487
2488fn complexity_panel(bench: &Benchmark, languages: &[Language]) -> Element {
2493 let mut sizes: Vec<(String, f64)> = bench.sizes.iter()
2494 .filter(|s| bench.scaling.contains_key(s.as_str()))
2495 .filter_map(|s| s.parse::<f64>().ok().map(|n| (s.clone(), n)))
2496 .collect();
2497 sizes.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
2498
2499 let time_rows: Vec<(String, String, f64)> = languages.iter().filter_map(|l| {
2500 let pts: Vec<(f64, f64)> = sizes.iter().filter_map(|(s, n)| {
2501 bench.scaling.get(s).and_then(|m| m.get(&l.id))
2502 .filter(|t| t.median_ms > 0.0)
2503 .map(|t| (*n, t.median_ms))
2504 }).collect();
2505 empirical_exponent(&pts).map(|e| (l.color.clone(), l.label.clone(), e))
2506 }).collect();
2507
2508 let space_rows: Vec<(String, String, f64)> = match &bench.memory {
2509 Some(mem) => {
2510 let mut ms: Vec<(String, f64)> = mem.by_size.keys()
2511 .filter_map(|s| s.parse::<f64>().ok().map(|n| (s.clone(), n)))
2512 .collect();
2513 ms.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
2514 languages.iter().filter_map(|l| {
2515 let pts: Vec<(f64, f64)> = ms.iter().filter_map(|(s, n)| {
2516 mem.by_size.get(s).and_then(|m| m.get(&l.id)).and_then(|v| *v)
2517 .filter(|kb| *kb > 0.0).map(|kb| (*n, kb))
2518 }).collect();
2519 empirical_exponent(&pts).map(|e| (l.color.clone(), l.label.clone(), e))
2520 }).collect()
2521 }
2522 None => Vec::new(),
2523 };
2524
2525 if bench.complexity.is_none() && time_rows.is_empty() && space_rows.is_empty() {
2526 return rsx! {};
2527 }
2528
2529 rsx! {
2530 div { class: "bench-complexity",
2531 div { class: "bench-complexity-title", "Complexity" }
2532 if let Some(c) = &bench.complexity {
2533 div { class: "bench-complexity-declared",
2534 span { class: "bench-complexity-chip",
2535 "time " strong { "{c.time}" }
2536 }
2537 span { class: "bench-complexity-chip",
2538 "space " strong { "{c.space}" }
2539 }
2540 }
2541 }
2542 if !time_rows.is_empty() {
2543 div { class: "bench-complexity-grid",
2544 div { class: "bench-complexity-col-title", "Measured time growth" }
2545 for (color, label, e) in time_rows.iter() {
2546 div { class: "bench-complexity-row",
2547 span { class: "bench-scaling-legend-dot", style: "background: {color};" }
2548 span { class: "bench-complexity-lang", "{label}" }
2549 span { class: "bench-complexity-exp", "t \u{2248} n^{e:.2}" }
2550 }
2551 }
2552 }
2553 }
2554 if !space_rows.is_empty() {
2555 div { class: "bench-complexity-grid",
2556 div { class: "bench-complexity-col-title", "Measured space growth" }
2557 for (color, label, e) in space_rows.iter() {
2558 div { class: "bench-complexity-row",
2559 span { class: "bench-scaling-legend-dot", style: "background: {color};" }
2560 span { class: "bench-complexity-lang", "{label}" }
2561 span { class: "bench-complexity-exp", "rss \u{2248} n^{e:.2}" }
2562 }
2563 }
2564 }
2565 }
2566 }
2567 }
2568}
2569
2570const BENCHMARKS_STYLE: &str = r#"
2571.bench-opt-toggles {
2572 display: flex;
2573 flex-direction: column;
2574 align-items: flex-start;
2575 gap: 6px;
2576 margin: 16px 0 4px;
2577}
2578.bench-opt-toggle {
2579 display: inline-flex;
2580 align-items: center;
2581 gap: 6px;
2582 padding: 4px 10px;
2583 border: 1px solid rgba(255,255,255,0.12);
2584 border-radius: 6px;
2585 font-size: 12px;
2586 cursor: pointer;
2587 user-select: none;
2588 background: rgba(255,255,255,0.04);
2589 color: #cdd3e0;
2590 transition: border-color .15s, color .15s;
2591}
2592.bench-opt-toggle.off {
2593 border-color: rgba(255,120,120,0.55);
2594 color: #ff9a9a;
2595 background: rgba(255,90,90,0.06);
2596}
2597.bench-opt-toggle input { cursor: pointer; margin: 0; }
2598.bench-opt-toggle.on.firing {
2599 border-color: rgba(0,212,255,0.6);
2600 color: #00d4ff;
2601 background: rgba(0,212,255,0.08);
2602 box-shadow: 0 0 10px rgba(0,212,255,0.18);
2603}
2604.bench-opt-toggle.on.enabling {
2605 border-style: dashed;
2606 border-color: rgba(167,139,250,0.45);
2607 color: rgba(205,211,224,0.75);
2608}
2609.bench-opt-toggle.on.preempted {
2610 border-style: dashed;
2611 border-color: rgba(247,164,29,0.35);
2612 color: rgba(205,211,224,0.55);
2613 background: rgba(247,164,29,0.04);
2614}
2615.bench-tree-row {
2616 display: flex;
2617 align-items: center;
2618 gap: 4px;
2619}
2620.bench-tree-chevron {
2621 display: inline-block;
2622 width: 12px;
2623 flex: 0 0 12px;
2624 font-size: 9px;
2625 color: rgba(229,231,235,0.5);
2626 cursor: pointer;
2627 transition: transform 0.2s ease, color 0.15s;
2628 text-align: center;
2629}
2630.bench-tree-chevron:hover { color: #e5e7eb; }
2631.bench-tree-chevron.open { transform: rotate(90deg); }
2632.bench-tree-spacer {
2633 display: inline-block;
2634 width: 12px;
2635 flex: 0 0 12px;
2636}
2637.bench-opt-master {
2638 display: flex;
2639 align-items: center;
2640 gap: 12px;
2641 flex-wrap: wrap;
2642 margin: 16px 0 6px;
2643}
2644.bench-opt-hint {
2645 font-size: 12px;
2646 color: rgba(229,231,235,0.5);
2647}
2648.bench-opt-rel {
2649 font-size: 10px;
2650 margin-left: 5px;
2651 padding: 1px 5px;
2652 border-radius: 4px;
2653 background: rgba(255,255,255,0.05);
2654 white-space: nowrap;
2655}
2656.bench-opt-rel.needs { color: #a78bfa; }
2657.bench-opt-rel.beats { color: #f7a41d; }
2658.bench-opt-rel.enables { color: #a78bfa; font-style: italic; }
2659.bench-opt-rel.depends { color: #6ee7b7; }
2660.bench-opt-rel.beaten { color: #f7a41d; font-style: italic; }
2661.bench-opt-rel.beats-now {
2662 color: #1a1410;
2663 background: rgba(247,164,29,0.85);
2664 font-weight: 700;
2665}
2666.bench-compiling {
2667 display: inline-flex;
2668 align-items: center;
2669 gap: 8px;
2670 color: #00d4ff;
2671 font-size: 12px;
2672 font-weight: 500;
2673}
2674.bench-compiling::before {
2675 content: "";
2676 width: 11px;
2677 height: 11px;
2678 border: 2px solid rgba(0,212,255,0.25);
2679 border-top-color: #00d4ff;
2680 border-radius: 50%;
2681 animation: bench-spin 0.6s linear infinite;
2682}
2683@keyframes bench-spin {
2684 to { transform: rotate(360deg); }
2685}
2686.bench-container {
2687 min-height: 100vh;
2688 background: linear-gradient(135deg, #070a12 0%, #0b1022 50%, #070a12 100%);
2689 color: #e5e7eb;
2690 font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
2691}
2692
2693.bench-hero {
2694 text-align: center;
2695 padding: 60px 20px 20px;
2696 max-width: 800px;
2697 margin: 0 auto;
2698}
2699
2700.bench-hero h1 {
2701 font-size: 42px;
2702 font-weight: 800;
2703 letter-spacing: -1px;
2704 background: linear-gradient(180deg, #ffffff 0%, rgba(229,231,235,0.78) 100%);
2705 -webkit-background-clip: text;
2706 -webkit-text-fill-color: transparent;
2707 margin-bottom: 12px;
2708}
2709
2710.bench-hero p {
2711 color: rgba(229,231,235,0.72);
2712 font-size: 18px;
2713 line-height: 1.6;
2714 margin-bottom: 20px;
2715}
2716
2717.bench-gate-status {
2718 color: rgba(229,231,235,0.72);
2719 font-size: 18px;
2720 padding: 40px 0 12px;
2721}
2722
2723.bench-gate-status.error {
2724 color: #fca5a5;
2725}
2726
2727.bench-gate-retry {
2728 padding: 10px 28px;
2729 border-radius: 10px;
2730 border: 1px solid rgba(255,255,255,0.2);
2731 background: rgba(255,255,255,0.08);
2732 color: #e8e8e8;
2733 font-size: 15px;
2734 cursor: pointer;
2735}
2736
2737.bench-gate-retry:hover {
2738 background: rgba(255,255,255,0.16);
2739}
2740
2741.bench-pills {
2742 display: flex;
2743 flex-wrap: wrap;
2744 gap: 8px;
2745 justify-content: center;
2746 margin-bottom: 16px;
2747}
2748
2749.bench-pill {
2750 display: inline-flex;
2751 align-items: center;
2752 gap: 4px;
2753 font-size: 12px;
2754 padding: 5px 12px;
2755 border-radius: 16px;
2756 background: rgba(255,255,255,0.05);
2757 border: 1px solid rgba(255,255,255,0.10);
2758 color: rgba(229,231,235,0.72);
2759 text-decoration: none;
2760 transition: all 0.2s ease;
2761}
2762
2763.bench-pill:hover {
2764 background: rgba(255,255,255,0.08);
2765 border-color: rgba(255,255,255,0.15);
2766}
2767
2768.bench-pill strong {
2769 color: #e5e7eb;
2770}
2771
2772.bench-pill.link {
2773 cursor: pointer;
2774 color: #00d4ff;
2775}
2776
2777.bench-section-nav {
2778 display: flex;
2779 justify-content: center;
2780 gap: 6px;
2781 padding: 12px 20px;
2782 margin-bottom: 24px;
2783 flex-wrap: wrap;
2784}
2785
2786.bench-section-nav a {
2787 font-size: 12px;
2788 font-weight: 500;
2789 padding: 5px 14px;
2790 border-radius: 16px;
2791 background: rgba(255,255,255,0.04);
2792 border: 1px solid rgba(255,255,255,0.08);
2793 color: rgba(229,231,235,0.6);
2794 text-decoration: none;
2795 transition: all 0.2s ease;
2796 backdrop-filter: blur(12px);
2797}
2798
2799.bench-section-nav a:hover {
2800 background: rgba(255,255,255,0.08);
2801 color: #e5e7eb;
2802 border-color: rgba(255,255,255,0.15);
2803}
2804
2805.bench-tabbar {
2806 display: flex;
2807 justify-content: center;
2808 gap: 4px;
2809 max-width: 1000px;
2810 margin: 0 auto 28px;
2811 padding: 4px;
2812 background: rgba(255,255,255,0.03);
2813 border: 1px solid rgba(255,255,255,0.08);
2814 border-radius: 14px;
2815 width: fit-content;
2816 flex-wrap: wrap;
2817}
2818
2819.bench-tabbar-btn {
2820 padding: 10px 22px;
2821 border: none;
2822 border-radius: 10px;
2823 background: transparent;
2824 color: rgba(229,231,235,0.55);
2825 font-size: 14px;
2826 font-weight: 600;
2827 letter-spacing: 0.2px;
2828 cursor: pointer;
2829 transition: all 0.2s ease;
2830}
2831
2832.bench-tabbar-btn:hover {
2833 color: #e5e7eb;
2834 background: rgba(255,255,255,0.05);
2835}
2836
2837.bench-tabbar-btn.active {
2838 background: linear-gradient(135deg, rgba(0,212,255,0.18), rgba(167,139,250,0.18));
2839 color: #e5e7eb;
2840 box-shadow: 0 2px 10px rgba(0,212,255,0.12);
2841}
2842
2843.bench-content {
2844 max-width: 1000px;
2845 margin: 0 auto;
2846 padding: 0 20px 80px;
2847}
2848
2849.bench-summary {
2850 display: grid;
2851 grid-template-columns: repeat(3, 1fr);
2852 gap: 16px;
2853 margin-bottom: 40px;
2854}
2855
2856.bench-summary-card {
2857 background: rgba(255,255,255,0.03);
2858 border: 1px solid rgba(255,255,255,0.08);
2859 border-radius: 16px;
2860 padding: 24px;
2861 text-align: center;
2862 transition: all 0.2s ease;
2863}
2864
2865.bench-summary-card:hover {
2866 background: rgba(255,255,255,0.05);
2867 border-color: rgba(255,255,255,0.12);
2868}
2869
2870.bench-summary-value {
2871 font-size: 36px;
2872 font-weight: 800;
2873 letter-spacing: -1px;
2874 margin-bottom: 4px;
2875}
2876
2877.bench-summary-eyebrow {
2878 font-size: 11px;
2879 font-weight: 600;
2880 text-transform: uppercase;
2881 letter-spacing: 0.5px;
2882 color: rgba(229,231,235,0.5);
2883 margin-bottom: 8px;
2884}
2885
2886.bench-summary-value.cyan { color: #00d4ff; }
2887.bench-summary-value.green { color: #22c55e; }
2888.bench-summary-value.purple { color: #a78bfa; }
2889
2890.bench-summary-label {
2891 font-size: 13px;
2892 color: rgba(229,231,235,0.56);
2893}
2894
2895.bench-tabs {
2896 display: flex;
2897 gap: 6px;
2898 margin-bottom: 24px;
2899 flex-wrap: wrap;
2900}
2901
2902.bench-tab {
2903 padding: 8px 16px;
2904 border-radius: 8px;
2905 border: 1px solid rgba(255,255,255,0.1);
2906 background: rgba(255,255,255,0.03);
2907 color: #94a3b8;
2908 cursor: pointer;
2909 font-size: 13px;
2910 font-weight: 500;
2911 transition: all 0.2s ease;
2912}
2913
2914.bench-tab:hover {
2915 background: rgba(255,255,255,0.08);
2916 color: #e8e8e8;
2917}
2918
2919.bench-tab.active {
2920 background: linear-gradient(135deg, rgba(0,212,255,0.25), rgba(129,140,248,0.25));
2921 color: #00d4ff;
2922 border-color: rgba(0,212,255,0.4);
2923}
2924
2925.bench-section {
2926 background: rgba(255,255,255,0.03);
2927 border: 1px solid rgba(255,255,255,0.08);
2928 border-radius: 16px;
2929 padding: 24px;
2930 margin-bottom: 24px;
2931}
2932
2933.bench-section-title {
2934 font-size: 18px;
2935 font-weight: 700;
2936 color: #fff;
2937 margin-bottom: 6px;
2938}
2939
2940.bench-section-desc {
2941 font-size: 13px;
2942 color: rgba(229,231,235,0.56);
2943 margin-bottom: 20px;
2944}
2945
2946.bench-chart {
2947 display: flex;
2948 flex-direction: column;
2949 gap: 6px;
2950}
2951
2952.bench-tier-label {
2953 font-size: 10px;
2954 font-weight: 600;
2955 text-transform: uppercase;
2956 letter-spacing: 0.5px;
2957 color: rgba(229,231,235,0.4);
2958 margin-top: 10px;
2959 margin-bottom: 2px;
2960}
2961
2962.bench-bar-row {
2963 display: flex;
2964 align-items: center;
2965 gap: 12px;
2966}
2967
2968.bench-bar-label {
2969 width: 130px;
2970 text-align: right;
2971 font-size: 13px;
2972 font-weight: 500;
2973 color: rgba(229,231,235,0.8);
2974 flex-shrink: 0;
2975}
2976
2977.bench-bar-track {
2978 flex: 1;
2979 min-width: 0;
2980 height: 28px;
2981 background: rgba(255,255,255,0.02);
2982 border-radius: 6px;
2983 overflow: hidden;
2984 position: relative;
2985}
2986
2987.bench-bar-fill {
2988 height: 100%;
2989 border-radius: 6px;
2990 display: flex;
2991 align-items: center;
2992 justify-content: flex-end;
2993 padding-right: 8px;
2994 min-width: 2px;
2995 transition: width 0.4s ease;
2996}
2997
2998.bench-bar-fill.logos-highlight {
2999 box-shadow: 0 0 16px rgba(0,212,255,0.35);
3000}
3001
3002.bench-bar-time {
3003 font-size: 11px;
3004 font-weight: 600;
3005 color: rgba(0,0,0,0.8);
3006 white-space: nowrap;
3007}
3008
3009.bench-bar-time-outside {
3010 font-size: 11px;
3011 font-weight: 600;
3012 color: rgba(229,231,235,0.6);
3013 white-space: nowrap;
3014 margin-left: 8px;
3015 flex-shrink: 0;
3016}
3017
3018/* Codec charts: the bar shows magnitude only; the exact value lives in its own
3019 always-visible right column, so even the smallest (winning) bar never hides its number. */
3020.bench-codec-track {
3021 flex: 1;
3022 height: 22px;
3023 background: rgba(255,255,255,0.04);
3024 border-radius: 6px;
3025 position: relative;
3026}
3027
3028.bench-codec-fill {
3029 height: 100%;
3030 border-radius: 6px;
3031 min-width: 3px;
3032 transition: width 0.4s ease;
3033}
3034
3035.bench-codec-fill.logos-highlight {
3036 box-shadow: 0 0 14px rgba(0,212,255,0.5);
3037}
3038
3039.bench-codec-val {
3040 width: 92px;
3041 text-align: right;
3042 font-size: 12px;
3043 font-weight: 600;
3044 color: rgba(229,231,235,0.75);
3045 flex-shrink: 0;
3046 font-variant-numeric: tabular-nums;
3047}
3048
3049.bench-codec-val.logos-val {
3050 color: #38e1ff;
3051}
3052
3053.bench-source {
3054 display: grid;
3055 grid-template-columns: 1fr 1fr;
3056 gap: 16px;
3057}
3058
3059.bench-source-panel {
3060 display: flex;
3061 flex-direction: column;
3062}
3063
3064.bench-source-header {
3065 font-size: 11px;
3066 font-weight: 600;
3067 text-transform: uppercase;
3068 letter-spacing: 0.5px;
3069 padding: 8px 12px;
3070 border-radius: 8px 8px 0 0;
3071 border: 1px solid rgba(255,255,255,0.08);
3072 border-bottom: none;
3073}
3074
3075.bench-source-header.logos {
3076 background: rgba(0,212,255,0.1);
3077 color: #00d4ff;
3078}
3079
3080.bench-source-header.rust {
3081 background: rgba(222,165,132,0.1);
3082 color: #dea584;
3083}
3084
3085.bench-source-code {
3086 background: rgba(0,0,0,0.3);
3087 border: 1px solid rgba(255,255,255,0.08);
3088 border-radius: 0 0 8px 8px;
3089 padding: 16px;
3090 font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
3091 font-size: 12px;
3092 line-height: 1.5;
3093 color: #e8e8e8;
3094 white-space: pre-wrap;
3095 overflow-x: auto;
3096 flex: 1;
3097}
3098
3099/* Collapsible pattern */
3100.bench-collapsible-btn {
3101 display: flex;
3102 align-items: center;
3103 gap: 8px;
3104 width: 100%;
3105 padding: 10px 0;
3106 background: none;
3107 border: none;
3108 border-top: 1px solid rgba(255,255,255,0.06);
3109 color: rgba(229,231,235,0.6);
3110 font-size: 13px;
3111 font-weight: 500;
3112 cursor: pointer;
3113 transition: color 0.2s ease;
3114 margin-top: 16px;
3115 text-align: left;
3116}
3117
3118.bench-collapsible-btn:hover {
3119 color: #e5e7eb;
3120}
3121
3122.bench-collapsible-chevron {
3123 display: inline-block;
3124 font-size: 10px;
3125 transition: transform 0.2s ease;
3126}
3127
3128.bench-collapsible-chevron.open {
3129 transform: rotate(90deg);
3130}
3131
3132.bench-collapsible-body {
3133 overflow: hidden;
3134 max-height: 0;
3135 transition: max-height 0.3s ease;
3136}
3137
3138.bench-collapsible-body.open {
3139 max-height: 8000px;
3140}
3141
3142/* Stats table */
3143.bench-stats-table {
3144 width: 100%;
3145 border-collapse: collapse;
3146 font-size: 12px;
3147 margin-top: 12px;
3148}
3149
3150.bench-stats-table th {
3151 text-align: left;
3152 font-size: 10px;
3153 font-weight: 600;
3154 text-transform: uppercase;
3155 letter-spacing: 0.5px;
3156 color: rgba(229,231,235,0.5);
3157 padding: 6px 8px;
3158 border-bottom: 1px solid rgba(255,255,255,0.08);
3159}
3160
3161.bench-stats-table td {
3162 padding: 6px 8px;
3163 color: rgba(229,231,235,0.7);
3164 border-bottom: 1px solid rgba(255,255,255,0.04);
3165 font-family: 'SF Mono', 'Fira Code', monospace;
3166 font-size: 11px;
3167}
3168
3169.bench-stats-table tr.highlight td {
3170 color: #00d4ff;
3171 background: rgba(0,212,255,0.05);
3172}
3173
3174/* Language source collapsible */
3175.bench-lang-collapsible {
3176 border: 1px solid rgba(255,255,255,0.06);
3177 border-radius: 8px;
3178 margin-bottom: 8px;
3179 overflow: hidden;
3180}
3181
3182.bench-lang-header {
3183 display: flex;
3184 align-items: center;
3185 gap: 10px;
3186 padding: 10px 14px;
3187 background: rgba(255,255,255,0.02);
3188 cursor: pointer;
3189 transition: background 0.2s ease;
3190}
3191
3192.bench-lang-header:hover {
3193 background: rgba(255,255,255,0.05);
3194}
3195
3196.bench-lang-dot {
3197 width: 8px;
3198 height: 8px;
3199 border-radius: 50%;
3200 flex-shrink: 0;
3201}
3202
3203.bench-lang-name {
3204 font-size: 13px;
3205 font-weight: 500;
3206 color: #e5e7eb;
3207 flex: 1;
3208}
3209
3210.bench-lang-version {
3211 font-size: 11px;
3212 color: rgba(229,231,235,0.4);
3213}
3214
3215.bench-lang-link {
3216 font-size: 11px;
3217 color: #00d4ff;
3218 text-decoration: none;
3219 opacity: 0.7;
3220 transition: opacity 0.2s ease;
3221}
3222
3223.bench-lang-link:hover {
3224 opacity: 1;
3225 text-decoration: underline;
3226}
3227
3228.bench-lang-code {
3229 background: rgba(0,0,0,0.3);
3230 padding: 16px;
3231 font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
3232 font-size: 12px;
3233 line-height: 1.5;
3234 color: #e8e8e8;
3235 white-space: pre-wrap;
3236 overflow-x: auto;
3237 max-height: 0;
3238 transition: max-height 0.3s ease, padding 0.3s ease;
3239 padding-top: 0;
3240 padding-bottom: 0;
3241}
3242
3243.bench-lang-code.open {
3244 max-height: 4000px;
3245 padding-top: 16px;
3246 padding-bottom: 16px;
3247}
3248
3249/* Compile chart */
3250.bench-compile-table {
3251 width: 100%;
3252 border-collapse: collapse;
3253}
3254
3255.bench-compile-table th {
3256 text-align: left;
3257 font-size: 11px;
3258 font-weight: 600;
3259 text-transform: uppercase;
3260 letter-spacing: 0.5px;
3261 color: rgba(229,231,235,0.5);
3262 padding: 8px 12px;
3263 border-bottom: 1px solid rgba(255,255,255,0.08);
3264}
3265
3266.bench-compile-table td {
3267 padding: 8px 12px;
3268 font-size: 13px;
3269 color: rgba(229,231,235,0.8);
3270 border-bottom: 1px solid rgba(255,255,255,0.04);
3271}
3272
3273.bench-compile-table tr:last-child td {
3274 border-bottom: none;
3275}
3276
3277.bench-compile-table .compiler-name {
3278 font-weight: 500;
3279 color: #e5e7eb;
3280}
3281
3282.bench-compile-table tr.highlight td {
3283 color: #00d4ff;
3284}
3285
3286/* Methodology */
3287.bench-methodology {
3288 color: rgba(229,231,235,0.56);
3289 font-size: 13px;
3290 line-height: 1.7;
3291}
3292
3293.bench-methodology ul {
3294 padding-left: 20px;
3295 margin: 8px 0;
3296}
3297
3298.bench-methodology li {
3299 margin-bottom: 4px;
3300}
3301
3302.bench-methodology a {
3303 color: #00d4ff;
3304 text-decoration: none;
3305}
3306
3307.bench-methodology a:hover {
3308 text-decoration: underline;
3309}
3310
3311.bench-methodology h3 {
3312 font-size: 14px;
3313 font-weight: 600;
3314 color: #e5e7eb;
3315 margin: 16px 0 8px;
3316}
3317
3318.bench-version-table {
3319 width: 100%;
3320 border-collapse: collapse;
3321 margin: 8px 0 16px;
3322}
3323
3324.bench-version-table th {
3325 text-align: left;
3326 font-size: 11px;
3327 font-weight: 600;
3328 text-transform: uppercase;
3329 letter-spacing: 0.5px;
3330 color: rgba(229,231,235,0.5);
3331 padding: 6px 10px;
3332 border-bottom: 1px solid rgba(255,255,255,0.08);
3333}
3334
3335.bench-version-table td {
3336 padding: 6px 10px;
3337 font-size: 12px;
3338 color: rgba(229,231,235,0.7);
3339 border-bottom: 1px solid rgba(255,255,255,0.04);
3340}
3341
3342/* Algorithmic-collapse note + badges */
3343.bench-summary-value sup {
3344 font-size: 18px;
3345 color: rgba(0,212,255,0.7);
3346 font-weight: 700;
3347}
3348
3349.bench-note {
3350 background: rgba(0,212,255,0.04);
3351 border: 1px solid rgba(0,212,255,0.15);
3352 border-radius: 12px;
3353 padding: 14px 18px;
3354 margin-bottom: 32px;
3355 font-size: 13px;
3356 line-height: 1.6;
3357 color: rgba(229,231,235,0.7);
3358}
3359
3360.bench-note strong { color: #00d4ff; }
3361
3362.bench-tab-badge {
3363 display: inline-block;
3364 margin-left: 6px;
3365 font-size: 10px;
3366}
3367
3368.bench-callout {
3369 display: flex;
3370 gap: 10px;
3371 align-items: flex-start;
3372 background: rgba(0,212,255,0.05);
3373 border: 1px solid rgba(0,212,255,0.18);
3374 border-radius: 10px;
3375 padding: 12px 14px;
3376 margin-bottom: 18px;
3377 font-size: 13px;
3378 line-height: 1.55;
3379 color: rgba(229,231,235,0.8);
3380}
3381
3382.bench-callout-icon {
3383 flex-shrink: 0;
3384 font-size: 15px;
3385 line-height: 1.4;
3386}
3387
3388.bench-chart-hint {
3389 font-size: 11px;
3390 color: rgba(229,231,235,0.4);
3391 margin-bottom: 10px;
3392}
3393
3394/* Scaling curve (inline SVG) */
3395.bench-scaling {
3396 margin-top: 8px;
3397}
3398
3399.bench-scaling svg {
3400 width: 100%;
3401 height: auto;
3402 display: block;
3403}
3404
3405.bench-scaling-legend {
3406 display: flex;
3407 flex-wrap: wrap;
3408 gap: 10px 16px;
3409 margin-top: 10px;
3410}
3411
3412.bench-scaling-legend-item {
3413 display: inline-flex;
3414 align-items: center;
3415 gap: 6px;
3416 font-size: 11px;
3417 color: rgba(229,231,235,0.6);
3418}
3419
3420.bench-scaling-legend-dot {
3421 width: 10px;
3422 height: 3px;
3423 border-radius: 2px;
3424}
3425
3426/* Complexity panel + memory bars */
3427.bench-complexity {
3428 margin: 20px 0 4px;
3429 padding: 14px 16px;
3430 border: 1px solid rgba(255,255,255,0.08);
3431 border-radius: 8px;
3432 background: rgba(255,255,255,0.02);
3433}
3434.bench-complexity-title {
3435 font-size: 11px;
3436 font-weight: 700;
3437 text-transform: uppercase;
3438 letter-spacing: 0.5px;
3439 color: rgba(229,231,235,0.45);
3440 margin-bottom: 10px;
3441}
3442.bench-complexity-declared {
3443 display: flex;
3444 gap: 10px;
3445 flex-wrap: wrap;
3446 margin-bottom: 12px;
3447}
3448.bench-complexity-chip {
3449 font-size: 12px;
3450 color: rgba(229,231,235,0.6);
3451 padding: 4px 10px;
3452 border-radius: 6px;
3453 background: rgba(255,255,255,0.04);
3454 border: 1px solid rgba(255,255,255,0.08);
3455}
3456.bench-complexity-chip strong { color: #93c5fd; font-weight: 700; margin-left: 4px; }
3457.bench-complexity-grid {
3458 display: flex;
3459 flex-direction: column;
3460 gap: 4px;
3461 margin-top: 8px;
3462}
3463.bench-complexity-col-title {
3464 font-size: 10px;
3465 text-transform: uppercase;
3466 letter-spacing: 0.4px;
3467 color: rgba(229,231,235,0.35);
3468 margin-bottom: 2px;
3469}
3470.bench-complexity-row {
3471 display: flex;
3472 align-items: center;
3473 gap: 8px;
3474 font-size: 12px;
3475}
3476.bench-complexity-lang { color: rgba(229,231,235,0.7); min-width: 90px; }
3477.bench-complexity-exp { color: rgba(229,231,235,0.5); font-family: ui-monospace, monospace; }
3478.bench-mem { margin-top: 8px; }
3479
3480/* Interpreter-vs-V8 section bits */
3481.bench-engine-pill {
3482 display: inline-flex;
3483 align-items: center;
3484 gap: 4px;
3485 font-size: 11px;
3486 padding: 3px 10px;
3487 border-radius: 12px;
3488 background: rgba(255,140,0,0.1);
3489 border: 1px solid rgba(255,140,0,0.25);
3490 color: #ff8c00;
3491 margin-left: 8px;
3492}
3493
3494.bench-floor-badge {
3495 font-size: 10px;
3496 color: #fbbf24;
3497 margin-left: 8px;
3498 white-space: nowrap;
3499}
3500
3501.bench-bar-n {
3502 font-size: 10px;
3503 color: rgba(229,231,235,0.4);
3504 margin-left: 6px;
3505}
3506
3507@media (max-width: 768px) {
3508 .bench-hero h1 { font-size: 32px; }
3509 .bench-summary { grid-template-columns: 1fr; }
3510 .bench-source { grid-template-columns: 1fr; }
3511 .bench-bar-label { width: 80px; font-size: 11px; }
3512 .bench-summary-value { font-size: 28px; }
3513 .bench-section-nav { gap: 4px; }
3514 .bench-section-nav a { font-size: 11px; padding: 4px 10px; }
3515 .bench-tabbar { width: auto; }
3516 .bench-tabbar-btn { padding: 9px 14px; font-size: 12.5px; }
3517}
3518
3519@media (max-width: 480px) {
3520 .bench-hero h1 { font-size: 26px; }
3521 .bench-hero p { font-size: 15px; }
3522 .bench-content { padding: 0 12px 60px; }
3523}
3524"#;
3525
3526#[derive(Clone, Copy, PartialEq, Eq)]
3529enum BenchTab {
3530 Language,
3531 Solver,
3532 Codec,
3533}
3534
3535impl BenchTab {
3536 fn label(self) -> &'static str {
3537 match self {
3538 BenchTab::Language => "Programming Language",
3539 BenchTab::Solver => "SAT Solver",
3540 BenchTab::Codec => "Transport Codec",
3541 }
3542 }
3543
3544 fn all() -> [BenchTab; 3] {
3545 [BenchTab::Language, BenchTab::Solver, BenchTab::Codec]
3546 }
3547}
3548
3549#[component(lazy)]
3554pub fn Benchmarks() -> Element {
3555 #[cfg(not(target_arch = "wasm32"))]
3556 return rsx! { BenchmarksLoaded {} };
3557
3558 #[cfg(target_arch = "wasm32")]
3559 {
3560 let mut bundle = use_resource(load_bench_bundle);
3561 let state = bundle.read_unchecked();
3562 match &*state {
3563 Some(Ok(())) => rsx! { BenchmarksLoaded {} },
3564 Some(Err(e)) => rsx! {
3565 BenchShell {
3566 p { class: "bench-gate-status error", "Benchmark data failed to load: {e}" }
3567 button {
3568 class: "bench-gate-retry",
3569 onclick: move |_| bundle.restart(),
3570 "Retry"
3571 }
3572 }
3573 },
3574 None => rsx! {
3575 BenchShell {
3576 p { class: "bench-gate-status", "Loading benchmark data\u{2026}" }
3577 }
3578 },
3579 }
3580 }
3581}
3582
3583#[cfg(target_arch = "wasm32")]
3586#[component]
3587fn BenchShell(children: Element) -> Element {
3588 rsx! {
3589 PageHead {
3590 title: seo_pages::BENCHMARKS.title,
3591 description: seo_pages::BENCHMARKS.description,
3592 canonical_path: seo_pages::BENCHMARKS.canonical_path,
3593 }
3594 style { "{BENCHMARKS_STYLE}" }
3595 div { class: "bench-container",
3596 MainNav { active: ActivePage::Benchmarks, subtitle: Some("Performance") }
3597 section { class: "bench-hero",
3598 h1 { "Benchmarks" }
3599 {children}
3600 }
3601 Footer {}
3602 }
3603 }
3604}
3605
3606#[component]
3607fn BenchmarksLoaded() -> Element {
3608 let data = bench_data().expect("gated: the Benchmarks shell loads the bundle first");
3609 let mut active_bench = use_signal(|| 0usize);
3610 let mut bench_tab = use_signal(|| BenchTab::Language);
3611 let mut opt_toggles = use_signal(|| vec![true; logicaffeine_compile::optimization::REGISTRY.len()]);
3615 let mut live_rust = use_signal(|| Option::<String>::None);
3620 let mut compiling = use_signal(|| false);
3621 let mut compile_gen = use_signal(|| 0u64);
3622 let mut base_fired = use_signal(Vec::<&'static str>::new);
3629 let mut fired_now = use_signal(Vec::<&'static str>::new);
3630 let mut preempted_now = use_signal(Vec::<(&'static str, &'static str)>::new);
3631 let mut base_preempted = use_signal(Vec::<(&'static str, &'static str)>::new);
3632 let mut base_dependencies = use_signal(Vec::<(&'static str, &'static str)>::new);
3633 let mut expanded = use_signal(|| vec![true; logicaffeine_compile::optimization::REGISTRY.len()]);
3636 let mut stats_open = use_signal(|| false);
3637 let mut compile_detail_open = use_signal(|| false);
3638 let mut methodology_open = use_signal(|| false);
3639 let mut source_open: Signal<[bool; 10]> = use_signal(|| [false; 10]);
3640
3641 use_effect(move || {
3652 let idx = active_bench();
3653 let toggles = opt_toggles();
3654 let b = &data.benchmarks[idx];
3655 let kw = |s: &str| logicaffeine_compile::optimization::by_keyword(s).map(|o| o.meta().keyword);
3656 let baked_fired: Vec<&'static str> = b.fired.iter().filter_map(|s| kw(s)).collect();
3659 let baked_blockers: Vec<(&'static str, &'static str)> =
3660 b.blockers.iter().filter_map(|(w, l)| Some((kw(w)?, kw(l)?))).collect();
3661 let baked_deps: Vec<(&'static str, &'static str)> =
3662 b.dependencies.iter().filter_map(|(d, x)| Some((kw(d)?, kw(x)?))).collect();
3663 base_fired.set(baked_fired.clone());
3664 base_preempted.set(baked_blockers.clone());
3665 base_dependencies.set(baked_deps);
3666
3667 let disabled: Vec<&'static str> = logicaffeine_compile::optimization::REGISTRY
3668 .iter()
3669 .enumerate()
3670 .filter(|(i, _)| !toggles[*i])
3671 .map(|(_, m)| m.keyword)
3672 .collect();
3673 let cache_present = !b.generated_rust.trim().is_empty();
3674
3675 if disabled.is_empty() {
3678 fired_now.set(baked_fired);
3679 preempted_now.set(baked_blockers);
3680 live_rust.set(if cache_present { None } else { Some(b.generated_rust.clone()) });
3681 compiling.set(false);
3682 return;
3683 }
3684
3685 let decorated =
3688 logicaffeine_compile::optimization::decorate_source(&b.logos_source, &disabled);
3689 let gen = *compile_gen.peek() + 1;
3690 compile_gen.set(gen);
3691 compiling.set(true);
3692 spawn(async move {
3693 gloo_timers::future::TimeoutFuture::new(30).await;
3694 if *compile_gen.peek() != gen {
3695 return;
3696 }
3697 let (rust, fired, preempted) =
3698 logicaffeine_compile::compile::compile_to_rust_traced(&decorated)
3699 .unwrap_or_else(|e| (format!("// compile error: {e:?}"), Vec::new(), Vec::new()));
3700 if *compile_gen.peek() != gen {
3701 return;
3702 }
3703 fired_now.set(fired);
3704 preempted_now.set(preempted);
3705 live_rust.set(Some(rust));
3706 compiling.set(false);
3707 });
3708 });
3709
3710 let breadcrumbs = vec![
3711 BreadcrumbItem { name: "Home", path: "/" },
3712 BreadcrumbItem { name: "Benchmarks", path: "/benchmarks" },
3713 ];
3714 let schemas = vec![
3715 organization_schema(),
3716 webpage_schema("LOGOS Benchmarks", seo_pages::BENCHMARKS.description, "/benchmarks"),
3717 breadcrumb_schema(&breadcrumbs),
3718 ];
3719
3720 let logos_vs_c = data.summary.geometric_mean_speedup_vs_c
3721 .get("logos_release").copied().unwrap_or(0.0);
3722
3723 let interp = interp_data().expect("gated: the Benchmarks shell loads the bundle first");
3724 let logos_apples = logos_apples_geomean(data);
3726 let interp_speed = interp_speed_vs_v8(interp);
3727 let aot_speed = aot_speed_vs_v8(interp);
3728 let collapse_count = data.benchmarks.iter().filter(|b| collapse_note(&b.id).is_some()).count();
3729 let apples_count = data.benchmarks.len().saturating_sub(collapse_count);
3730
3731 let bench = &data.benchmarks[active_bench()];
3732 let bench_sources = bench_sources_for(&bench.id)
3733 .expect("sources_cover_every_benchmark locks every benchmark id to a sources entry");
3734
3735 let opt_tog = opt_toggles();
3742 let opt_disabled: Vec<&'static str> = logicaffeine_compile::optimization::REGISTRY
3743 .iter()
3744 .enumerate()
3745 .filter(|(i, _)| !opt_tog[*i])
3746 .map(|(_, m)| m.keyword)
3747 .collect();
3748 let opt_decorated =
3749 logicaffeine_compile::optimization::decorate_source(&bench.logos_source, &opt_disabled);
3750 let all_opts_on = opt_disabled.is_empty();
3751
3752 let opt_tree: Vec<logicaffeine_compile::optimization::OptNode> = {
3761 use logicaffeine_compile::optimization::by_keyword;
3762 let fired: Vec<_> = base_fired().iter().filter_map(|kw| by_keyword(kw)).collect();
3763 let preempted: Vec<_> = base_preempted()
3764 .iter()
3765 .filter_map(|(w, l)| Some((by_keyword(w)?, by_keyword(l)?)))
3766 .collect();
3767 let dependencies: Vec<_> = base_dependencies()
3768 .iter()
3769 .filter_map(|(d, x)| Some((by_keyword(d)?, by_keyword(x)?)))
3770 .collect();
3771 logicaffeine_compile::optimization::relationship_tree(&fired, &preempted, &dependencies)
3772 };
3773
3774 let tree_visible: Vec<bool> = {
3778 let exp = expanded();
3779 let mut vis = Vec::with_capacity(opt_tree.len());
3780 let mut hide_from: Option<usize> = None;
3781 for node in &opt_tree {
3782 if let Some(d) = hide_from {
3783 if node.depth <= d {
3784 hide_from = None;
3785 }
3786 }
3787 let visible = hide_from.is_none();
3788 if visible && node.has_children && !exp.get(node.opt as usize).copied().unwrap_or(true) {
3789 hide_from = Some(node.depth);
3790 }
3791 vis.push(visible);
3792 }
3793 vis
3794 };
3795
3796 let cache_ok = !bench.generated_rust.trim().is_empty();
3797 let rust_loading = (!all_opts_on || !cache_ok) && (compiling() || live_rust().is_none());
3798 let rust_text: String = if all_opts_on && cache_ok {
3799 bench.generated_rust.clone()
3800 } else {
3801 live_rust().unwrap_or_default()
3802 };
3803 let ref_size = if bench.scaling.contains_key(bench.reference_size.as_str()) {
3805 bench.reference_size.clone()
3806 } else {
3807 bench.sizes.last().cloned().unwrap_or_else(|| bench.reference_size.clone())
3808 };
3809 let ref_timings = bench.scaling.get(ref_size.as_str());
3810
3811 let mut chart_entries: Vec<(&str, &str, f64, &str, bool, bool)> = Vec::new();
3813 let ref_timeout = bench.timeouts.get(ref_size.as_str());
3814 if let Some(timings) = ref_timings {
3815 for lang in &data.languages {
3816 if lang.id == "js" { continue; }
3819 if let Some(t) = timings.get(&lang.id) {
3820 chart_entries.push((
3821 &lang.label,
3822 &lang.color,
3823 t.median_ms,
3824 &lang.tier,
3825 lang.id == "logos_release",
3826 false,
3827 ));
3828 } else if ref_timeout.is_some() {
3829 chart_entries.push((
3831 &lang.label,
3832 &lang.color,
3833 ref_timeout.unwrap() + 1.0, &lang.tier,
3835 lang.id == "logos_release",
3836 true,
3837 ));
3838 }
3839 }
3840 }
3841
3842 chart_entries.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(std::cmp::Ordering::Equal));
3843
3844 let compiled_max = chart_entries.iter()
3846 .filter(|e| e.3 != "interpreted" && !e.5)
3847 .map(|e| e.2)
3848 .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
3849 .unwrap_or(1.0);
3850
3851 let interpreted_max = chart_entries.iter()
3852 .filter(|e| e.3 == "interpreted" && !e.5)
3853 .map(|e| e.2)
3854 .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
3855 .unwrap_or(1.0);
3856
3857 let compiled_tier_order = ["systems", "managed", "transpiled"];
3858 let mut compiled_grouped: Vec<(&str, Vec<(&str, &str, f64, bool, bool)>)> = Vec::new();
3859 for &tier in &compiled_tier_order {
3860 let entries: Vec<_> = chart_entries.iter()
3861 .filter(|e| e.3 == tier)
3862 .map(|e| (e.0, e.1, e.2, e.4, e.5))
3863 .collect();
3864 if !entries.is_empty() {
3865 compiled_grouped.push((tier, entries));
3866 }
3867 }
3868
3869 let interpreted_flat: Vec<(&str, &str, f64, bool, bool)> = chart_entries.iter()
3870 .filter(|e| e.3 == "interpreted")
3871 .map(|e| (e.0, e.1, e.2, e.4, e.5))
3872 .collect();
3873
3874 let mut stats_entries: Vec<(&str, &str, Option<&TimingResult>)> = Vec::new();
3876 if let Some(timings) = ref_timings {
3877 for lang in &data.languages {
3878 if lang.id == "js" { continue; }
3879 if let Some(t) = timings.get(&lang.id) {
3880 stats_entries.push((&lang.label, &lang.id, Some(t)));
3881 } else if ref_timeout.is_some() {
3882 stats_entries.push((&lang.label, &lang.id, None));
3883 }
3884 }
3885 }
3886 stats_entries.sort_by(|a, b| {
3887 match (a.2, b.2) {
3888 (Some(ta), Some(tb)) => ta.median_ms.partial_cmp(&tb.median_ms).unwrap_or(std::cmp::Ordering::Equal),
3889 (Some(_), None) => std::cmp::Ordering::Less,
3890 (None, Some(_)) => std::cmp::Ordering::Greater,
3891 (None, None) => std::cmp::Ordering::Equal,
3892 }
3893 });
3894
3895 let mut compile_entries: Vec<(&str, f64, f64, bool)> = bench.compilation.iter()
3897 .map(|(name, r)| (name.as_str(), r.mean_ms, r.stddev_ms, name.starts_with("largo")))
3898 .collect();
3899 compile_entries.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
3900
3901 let compile_max = compile_entries.last().map(|e| e.1).unwrap_or(1.0);
3902
3903 let mut summary_entries: Vec<(&str, f64, &str, bool)> = Vec::new();
3905 for lang in &data.languages {
3906 if lang.id == "js" { continue; }
3907 if let Some(&val) = data.summary.geometric_mean_speedup_vs_c.get(&lang.id) {
3908 summary_entries.push((&lang.label, val, &lang.color, lang.id == "logos_release"));
3909 }
3910 }
3911 summary_entries.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
3912 let summary_max = summary_entries.first().map(|e| e.1).unwrap_or(1.0);
3913
3914 let interp_bench = interp.benchmarks.iter().find(|ib| ib.id == bench.id);
3916 let mut interp_bars: Vec<(&'static str, &'static str, f64, bool, bool)> = Vec::new();
3918 let mut interp_n = String::new();
3919 let mut interp_engine = String::new();
3920 let mut interp_active_speed: Option<f64> = None;
3921 if let Some(ib) = interp_bench {
3922 interp_n = ib.reference_size.clone();
3923 interp_engine = ib.interpreter_engine.clone();
3924 if let Some(t) = interp_ref(ib) {
3925 for id in ["logos_interp", "logos_tiered", "logos_aot", "js"] {
3929 if let Some(tr) = t.get(id) {
3930 let lbl = if id == "js" { "Node / V8" } else { lang_label(id) };
3931 let col = lang_color(id);
3932 let is_logos = id.starts_with("logos");
3933 interp_bars.push((lbl, col, tr.median_ms, is_logos, id == "js" && node_floored(tr)));
3934 }
3935 }
3936 if let (Some(j), Some(l)) = (t.get("js"), t.get("logos_interp")) {
3937 if l.median_ms > 0.0 {
3938 interp_active_speed = Some(j.median_ms / l.median_ms);
3939 }
3940 }
3941 }
3942 }
3943 interp_bars.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(std::cmp::Ordering::Equal));
3944 let interp_max = interp_bars.iter().map(|e| e.2).fold(0.0_f64, f64::max).max(1e-9);
3945 if interp_engine.is_empty() { interp_engine = "\u{2014}".to_string(); }
3946 let interp_offfloor = interp.benchmarks.iter().filter(|ib| {
3947 interp_ref(ib).and_then(|t| {
3948 let j = t.get("js")?;
3949 t.get("logos_interp")?;
3950 Some(!node_floored(j))
3951 }).unwrap_or(false)
3952 }).count();
3953 let interp_node_ver = if interp.metadata.node.is_empty() { "Node".to_string() } else { format!("Node {}", interp.metadata.node) };
3954
3955 let mut startup_bars: Vec<(&'static str, &'static str, f64, bool)> = Vec::new();
3958 for id in ["logos_interp", "js"] {
3959 if let Some(t) = interp.startup.engines.get(id) {
3960 if t.mean_ms > 0.0 {
3961 let lbl = match id { "logos_interp" => "LOGOS interp", "js" => "Node / V8", _ => id };
3962 let col = match id { "logos_interp" => "#ff8c00", "js" => "#f7df1e", _ => "#94a3b8" };
3963 startup_bars.push((lbl, col, t.mean_ms, id == "logos_interp"));
3964 }
3965 }
3966 }
3967 startup_bars.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(std::cmp::Ordering::Equal));
3968 let startup_max = startup_bars.iter().map(|e| e.2).fold(0.0_f64, f64::max).max(1e-9);
3969 let startup_logos = interp.startup.engines.get("logos_interp").map(|t| t.mean_ms).unwrap_or(0.0);
3970 let startup_node = interp.startup.engines.get("js").map(|t| t.mean_ms).unwrap_or(0.0);
3971 let startup_vs_v8 = if startup_logos > 0.0 { startup_node / startup_logos } else { 0.0 };
3972
3973 let mut engine_size_bars: Vec<(&'static str, &'static str, f64, Option<f64>, bool)> = Vec::new();
3975 let mut wasm_bundle_bytes = 0.0_f64;
3976 if let Some(es) = &interp.interpreter_sizes {
3977 for id in ["logos", "node", "deno", "bun"] {
3978 if let Some(s) = es.engines.get(id) {
3979 if s.as_built > 0.0 {
3980 let lbl = match id {
3981 "logos" => "largo (LOGOS VM+JIT)",
3982 "node" => "Node / V8",
3983 "deno" => "Deno",
3984 "bun" => "Bun",
3985 _ => id,
3986 };
3987 let col = match id { "logos" => "#ff8c00", "node" => "#f7df1e", _ => "#94a3b8" };
3988 engine_size_bars.push((lbl, col, s.as_built, s.stripped, id == "logos"));
3989 }
3990 }
3991 }
3992 wasm_bundle_bytes = es.wasm_bundle_bytes.unwrap_or(0.0);
3993 }
3994 engine_size_bars.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(std::cmp::Ordering::Equal));
3995 let engine_size_max = engine_size_bars.iter().map(|e| e.2).fold(0.0_f64, f64::max).max(1.0);
3996 let wasm_bundle_str = if wasm_bundle_bytes > 0.0 { format_bytes(wasm_bundle_bytes) } else { String::new() };
3997
3998 let source_langs = ["c", "cpp", "rust", "zig", "go", "java", "js", "python", "ruby", "nim"];
4000
4001 let commit_url = format!("{}/commit/{}", GITHUB_REPO, data.metadata.commit);
4002 let release_url = format!("{}/releases/tag/v{}", GITHUB_REPO, data.metadata.logos_version);
4003 let raw_json_url = format!("{}/blob/main/benchmarks/results/latest.json", GITHUB_REPO);
4004 let solver_meta = &solver_data().expect("gated: the Benchmarks shell loads the bundle first").metadata;
4006 let codec_meta = &codec_data().expect("gated: the Benchmarks shell loads the bundle first").metadata;
4007 let solver_json_url = format!("{}/blob/main/benchmarks/results/solvers.json", GITHUB_REPO);
4008 let codec_json_url = format!("{}/blob/main/benchmarks/results/latest-codec.json", GITHUB_REPO);
4009 let solver_date = solver_meta.date.get(..10).unwrap_or(solver_meta.date.as_str());
4010 let codec_date = codec_meta.date.get(..10).unwrap_or(codec_meta.date.as_str());
4011 let history_url = format!("{}/tree/main/benchmarks/results/history", GITHUB_REPO);
4012 let runsh_url = format!("{}/blob/main/benchmarks/run.sh", GITHUB_REPO);
4013 let bench_dir_url = format!("{}/tree/main/benchmarks", GITHUB_REPO);
4014
4015 rsx! {
4016 PageHead {
4017 title: seo_pages::BENCHMARKS.title,
4018 description: seo_pages::BENCHMARKS.description,
4019 canonical_path: seo_pages::BENCHMARKS.canonical_path,
4020 }
4021 style { "{BENCHMARKS_STYLE}" }
4022 JsonLdMultiple { schemas }
4023
4024 div { class: "bench-container",
4025 MainNav { active: ActivePage::Benchmarks, subtitle: Some("Performance") }
4026
4027 section { class: "bench-hero", id: "overview",
4029 h1 { "Benchmarks" }
4030 if bench_tab() == BenchTab::Language {
4031 p { "LOGOS compiles to Rust. Rust-level performance, English-level readability." }
4032 div { class: "bench-pills",
4033 a { class: "bench-pill", href: "{release_url}", target: "_blank",
4034 strong { "v{data.metadata.logos_version}" }
4035 }
4036 a { class: "bench-pill", href: "{commit_url}", target: "_blank",
4037 "{data.metadata.commit}"
4038 }
4039 span { class: "bench-pill", "{data.metadata.cpu}" }
4040 span { class: "bench-pill", "{data.metadata.os}" }
4041 span { class: "bench-pill", "{&data.metadata.date[..10]}" }
4042 a { class: "bench-pill link", href: "{raw_json_url}", target: "_blank",
4043 "Raw JSON"
4044 }
4045 }
4046 }
4047 if bench_tab() == BenchTab::Solver {
4048 p { "Certified proofs in milliseconds \u{2014} the same pure-Rust prover that runs in your browser, with no Z3." }
4049 div { class: "bench-pills",
4050 if !solver_meta.cpu.is_empty() {
4051 span { class: "bench-pill", "{solver_meta.cpu}" }
4052 }
4053 if !solver_meta.kissat.is_empty() {
4054 span { class: "bench-pill", "Kissat {solver_meta.kissat}" }
4055 }
4056 if !solver_meta.date.is_empty() {
4057 span { class: "bench-pill", "{solver_date}" }
4058 }
4059 a { class: "bench-pill link", href: "{solver_json_url}", target: "_blank",
4060 "Raw JSON"
4061 }
4062 }
4063 }
4064 if bench_tab() == BenchTab::Codec {
4065 p { "Smallest bytes on the wire \u{2014} the LOGOS codec, head-to-head against the industry serializers." }
4066 div { class: "bench-pills",
4067 if !codec_meta.cpu.is_empty() {
4068 span { class: "bench-pill", "{codec_meta.cpu}" }
4069 }
4070 if !codec_meta.os.is_empty() {
4071 span { class: "bench-pill", "{codec_meta.os}" }
4072 }
4073 if !codec_meta.date.is_empty() {
4074 span { class: "bench-pill", "{codec_date}" }
4075 }
4076 a { class: "bench-pill link", href: "{codec_json_url}", target: "_blank",
4077 "Raw JSON"
4078 }
4079 }
4080 }
4081 }
4082
4083 nav { class: "bench-tabbar",
4085 for t in BenchTab::all() {
4086 button {
4087 key: "{t.label()}",
4088 class: if bench_tab() == t { "bench-tabbar-btn active" } else { "bench-tabbar-btn" },
4089 onclick: move |_| bench_tab.set(t),
4090 "{t.label()}"
4091 }
4092 }
4093 }
4094
4095 if bench_tab() == BenchTab::Language {
4097 nav { class: "bench-section-nav",
4098 a { href: "#overview", "Overview" }
4099 a { href: "#performance", "Performance" }
4100 a { href: "#interpreter", "Interpreter" }
4101 a { href: "#source", "Source Code" }
4102 a { href: "#compilation", "Compilation" }
4103 a { href: "#summary", "Summary" }
4104 a { href: "#methodology", "Methodology" }
4105 }
4106 }
4107
4108 if bench_tab() == BenchTab::Language {
4109 div { class: "bench-content",
4110 div { class: "bench-summary",
4112 div { class: "bench-summary-card",
4113 div { class: "bench-summary-eyebrow", "LOGOS compiled vs C" }
4114 div { class: "bench-summary-value cyan",
4115 "{logos_vs_c:.2}x"
4116 sup { "*" }
4117 }
4118 div { class: "bench-summary-label", "the speed of C (geomean)" }
4119 }
4120 div { class: "bench-summary-card",
4121 div { class: "bench-summary-eyebrow", "Same algorithm as C" }
4122 div { class: "bench-summary-value green", "{logos_apples:.2}x" }
4123 div { class: "bench-summary-label", "the speed of C (geomean)" }
4124 }
4125 div { class: "bench-summary-card",
4126 div { class: "bench-summary-eyebrow", "Interpreted LOGOS vs V8" }
4127 div { class: "bench-summary-value purple", "{interp_speed:.2}x" }
4128 div { class: "bench-summary-label", "the speed of V8 (geomean)" }
4129 }
4130 if aot_speed > 0.0 {
4131 div { class: "bench-summary-card",
4132 div { class: "bench-summary-eyebrow", "AOT-native vs V8" }
4133 div { class: "bench-summary-value", style: "color:#00d4ff;", "{aot_speed:.2}x" }
4134 div { class: "bench-summary-label", "the speed of V8, warm (geomean)" }
4135 }
4136 }
4137 }
4138
4139 div { class: "bench-note",
4140 "The headline covers all {data.benchmarks.len()} benchmarks. On {collapse_count} of them the LOGOS "
4141 "compiler reduces the work itself, for example by folding a recursive function into a closed form, so "
4142 "it runs a faster algorithm than the C version rather than just faster machine code. Those wins are "
4143 "real, and the generated Rust for each is shown in the Source Code section below. The second number is "
4144 "the geometric mean over the remaining {apples_count} benchmarks, where LOGOS and C compile the same "
4145 "algorithm. Both numbers are \u{201c}x the speed of C\u{201d}, so higher is faster."
4146 }
4147
4148 div { class: "bench-tabs",
4150 for (i, b) in data.benchmarks.iter().enumerate() {
4151 button {
4152 key: "{i}",
4153 class: if active_bench() == i { "bench-tab active" } else { "bench-tab" },
4154 onclick: move |_| {
4155 active_bench.set(i);
4156 stats_open.set(false);
4157 compile_detail_open.set(false);
4158 source_open.set([false; 10]);
4159 opt_toggles.set(vec![true; logicaffeine_compile::optimization::REGISTRY.len()]);
4162 live_rust.set(None);
4163 compiling.set(false);
4164 base_fired.set(Vec::new());
4165 fired_now.set(Vec::new());
4166 preempted_now.set(Vec::new());
4167 base_preempted.set(Vec::new());
4168 base_dependencies.set(Vec::new());
4169 expanded.set(vec![true; logicaffeine_compile::optimization::REGISTRY.len()]);
4170 },
4171 "{b.name}"
4172 if collapse_note(&b.id).is_some() {
4173 span { class: "bench-tab-badge", title: "Algorithm collapsed by the LOGOS compiler", "\u{26a1}" }
4174 }
4175 }
4176 }
4177 }
4178
4179 div { class: "bench-section", id: "performance",
4181 div { class: "bench-section-title", "{bench.name}" }
4182 div { class: "bench-section-desc",
4183 "{bench.description} (n = {ref_size})"
4184 }
4185
4186 if let Some(note) = collapse_note(&bench.id) {
4187 div { class: "bench-callout",
4188 span { class: "bench-callout-icon", "\u{26a1}" }
4189 span { "{note}" }
4190 }
4191 }
4192
4193 div { class: "bench-chart-hint", "Wall-clock time at n = {ref_size} \u{2014} shorter bar is faster." }
4194
4195 div { class: "bench-chart",
4196 for (tier, entries) in compiled_grouped.iter() {
4197 div { class: "bench-tier-label", "{tier_label(tier)}" }
4198 for (label, color, median, is_logos, is_timeout) in entries.iter() {
4199 {
4200 if *is_timeout {
4201 let timeout_str = ref_timeout.map(|t| format_timeout(*t)).unwrap_or_else(|| ">timeout".to_string());
4202 rsx! {
4203 div { class: "bench-bar-row",
4204 div { class: "bench-bar-label", "{label}" }
4205 div { class: "bench-bar-track",
4206 div {
4207 class: "bench-bar-fill",
4208 style: "width: 100%; background: repeating-linear-gradient(45deg, {color}33, {color}33 10px, {color}11 10px, {color}11 20px);",
4209 }
4210 }
4211 span { class: "bench-bar-time-outside", "{timeout_str}" }
4212 }
4213 }
4214 } else {
4215 let pct = (*median / compiled_max * 100.0).min(100.0);
4216 let time_str = format_time(*median);
4217 let show_inside = pct > 15.0;
4218 let bar_class = if *is_logos { "bench-bar-fill logos-highlight" } else { "bench-bar-fill" };
4219 rsx! {
4220 div { class: "bench-bar-row",
4221 div { class: "bench-bar-label", "{label}" }
4222 div { class: "bench-bar-track",
4223 div {
4224 class: "{bar_class}",
4225 style: "width: {pct:.1}%; background: {color};",
4226 if show_inside {
4227 span { class: "bench-bar-time", "{time_str}" }
4228 }
4229 }
4230 }
4231 if !show_inside {
4232 span { class: "bench-bar-time-outside", "{time_str}" }
4233 }
4234 }
4235 }
4236 }
4237 }
4238 }
4239 }
4240
4241 if !interpreted_flat.is_empty() {
4242 div {
4243 style: "border-top: 1px solid rgba(255,255,255,0.08); margin: 16px 0 8px; padding-top: 8px; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: rgba(229,231,235,0.3);",
4244 "Interpreted"
4245 }
4246 for (label, color, median, is_logos, is_timeout) in interpreted_flat.iter() {
4247 {
4248 if *is_timeout {
4249 let timeout_str = ref_timeout.map(|t| format_timeout(*t)).unwrap_or_else(|| ">timeout".to_string());
4250 rsx! {
4251 div { class: "bench-bar-row",
4252 div { class: "bench-bar-label", "{label}" }
4253 div { class: "bench-bar-track",
4254 div {
4255 class: "bench-bar-fill",
4256 style: "width: 100%; background: repeating-linear-gradient(45deg, {color}33, {color}33 10px, {color}11 10px, {color}11 20px);",
4257 }
4258 }
4259 span { class: "bench-bar-time-outside", "{timeout_str}" }
4260 }
4261 }
4262 } else {
4263 let pct = (*median / interpreted_max * 100.0).min(100.0);
4264 let time_str = format_time(*median);
4265 let show_inside = pct > 15.0;
4266 let bar_class = if *is_logos { "bench-bar-fill logos-highlight" } else { "bench-bar-fill" };
4267 rsx! {
4268 div { class: "bench-bar-row",
4269 div { class: "bench-bar-label", "{label}" }
4270 div { class: "bench-bar-track",
4271 div {
4272 class: "{bar_class}",
4273 style: "width: {pct:.1}%; background: {color};",
4274 if show_inside {
4275 span { class: "bench-bar-time", "{time_str}" }
4276 }
4277 }
4278 }
4279 if !show_inside {
4280 span { class: "bench-bar-time-outside", "{time_str}" }
4281 }
4282 }
4283 }
4284 }
4285 }
4286 }
4287 }
4288 }
4289
4290 div {
4292 class: "bench-section-desc",
4293 style: "margin: 24px 0 6px; color: rgba(229,231,235,0.72); font-weight: 600;",
4294 "Scaling \u{2014} time vs problem size (log\u{2013}log)"
4295 }
4296 {scaling_chart(bench, &data.languages)}
4297
4298 {complexity_panel(bench, &data.languages)}
4299
4300 if bench.memory.is_some() {
4302 div {
4303 class: "bench-section-desc",
4304 style: "margin: 24px 0 6px; color: rgba(229,231,235,0.72); font-weight: 600;",
4305 "Memory \u{2014} peak resident set size"
4306 }
4307 {memory_bar_chart(bench, &data.languages)}
4308 }
4309
4310 if bench.binary_sizes.is_some() {
4312 div {
4313 class: "bench-section-desc",
4314 style: "margin: 24px 0 6px; color: rgba(229,231,235,0.72); font-weight: 600;",
4315 "Binary size \u{2014} compiled-artifact footprint"
4316 }
4317 {binary_size_bar_chart(bench, &data.languages)}
4318 div { class: "bench-note", style: "margin-top:12px;",
4319 "The size of the program you actually ship. C and C++ stay tiny because the runtime lives in the system libc; Rust and Go statically link their runtimes; LOGOS compiles to a compact self-contained binary in between. "
4320 "Java\u{2019}s figure is its bytecode alone \u{2014} it still needs the JVM to run \u{2014} and JavaScript has no compiled artifact at all (its footprint is the V8 engine, in the Interpreter section). "
4321 "As-built is the real shipped file; stripped removes debug symbols for a code-only comparison."
4322 }
4323 }
4324
4325 button {
4327 class: "bench-collapsible-btn",
4328 onclick: move |_| stats_open.set(!stats_open()),
4329 span {
4330 class: if stats_open() { "bench-collapsible-chevron open" } else { "bench-collapsible-chevron" },
4331 "\u{25b6}"
4332 }
4333 "Detailed Statistics"
4334 }
4335 div {
4336 class: if stats_open() { "bench-collapsible-body open" } else { "bench-collapsible-body" },
4337 table { class: "bench-stats-table",
4338 thead {
4339 tr {
4340 th { "Language" }
4341 th { "Mean" }
4342 th { "Median" }
4343 th { "StdDev" }
4344 th { "Min" }
4345 th { "Max" }
4346 th { "User" }
4347 th { "System" }
4348 th { "CV" }
4349 th { "Runs" }
4350 }
4351 }
4352 tbody {
4353 for (label, lid, maybe_t) in stats_entries.iter() {
4354 if let Some(t) = maybe_t {
4355 tr {
4356 class: if *lid == "logos_release" { "highlight" } else { "" },
4357 td { "{label}" }
4358 td { "{format_time(t.mean_ms)}" }
4359 td { "{format_time(t.median_ms)}" }
4360 td { "\u{00b1}{format_time(t.stddev_ms)}" }
4361 td { "{format_time(t.min_ms)}" }
4362 td { "{format_time(t.max_ms)}" }
4363 td { "{t.user_ms.map(|v| format_time(v)).unwrap_or_else(|| \"\u{2014}\".to_string())}" }
4364 td { "{t.system_ms.map(|v| format_time(v)).unwrap_or_else(|| \"\u{2014}\".to_string())}" }
4365 td { "{t.cv:.3}" }
4366 td { "{t.runs}" }
4367 }
4368 } else {
4369 {
4370 let timeout_str = ref_timeout.map(|t| format_timeout(*t)).unwrap_or_else(|| ">timeout".to_string());
4371 rsx! {
4372 tr {
4373 class: if *lid == "logos_release" { "highlight" } else { "" },
4374 td { "{label}" }
4375 td { "{timeout_str}" }
4376 td { "{timeout_str}" }
4377 td { "\u{2014}" }
4378 td { "\u{2014}" }
4379 td { "\u{2014}" }
4380 td { "\u{2014}" }
4381 td { "\u{2014}" }
4382 td { "\u{2014}" }
4383 td { "\u{2014}" }
4384 }
4385 }
4386 }
4387 }
4388 }
4389 }
4390 }
4391 }
4392 }
4393
4394 div { class: "bench-section", id: "interpreter",
4396 div { class: "bench-section-title", "LOGOS vs JavaScript / V8" }
4397 div { class: "bench-section-desc",
4398 "The LOGOS engine ladder \u{2014} bytecode VM, copy-and-patch JIT, and the warm AOT-native tier \u{2014} against {interp_node_ver} / V8."
4399 }
4400
4401 div { class: "bench-summary", style: "margin-bottom: 20px;",
4402 div { class: "bench-summary-card",
4403 div { class: "bench-summary-value cyan", "{startup_vs_v8:.1}x" }
4404 div { class: "bench-summary-label", "faster cold start than V8" }
4405 }
4406 div { class: "bench-summary-card",
4407 div { class: "bench-summary-value", style: "color:#ff8c00;", "{startup_logos:.1}ms" }
4408 div { class: "bench-summary-label", "LOGOS interpreter cold start" }
4409 }
4410 div { class: "bench-summary-card",
4411 div { class: "bench-summary-value purple", "{interp_speed:.2}x" }
4412 div { class: "bench-summary-label", "the speed of V8 on compute (geomean)" }
4413 }
4414 if aot_speed > 0.0 {
4415 div { class: "bench-summary-card",
4416 div { class: "bench-summary-value", style: "color:#00d4ff;", "{aot_speed:.2}x" }
4417 div { class: "bench-summary-label", "the speed of V8, AOT-native warm (geomean)" }
4418 }
4419 }
4420 }
4421
4422 if !startup_bars.is_empty() {
4424 div { class: "bench-chart-hint", style: "font-weight:600;color:rgba(229,231,235,0.72);margin-top:4px;",
4425 "Cold start \u{2014} launch the engine and run a trivial program ({interp.startup.runs} runs, shorter is faster)"
4426 }
4427 div { class: "bench-chart",
4428 for (label, color, mean, is_logos) in startup_bars.iter() {
4429 {
4430 let pct = (*mean / startup_max * 100.0).min(100.0);
4431 let time_str = format_time(*mean);
4432 let show_inside = pct > 18.0;
4433 let bar_class = if *is_logos { "bench-bar-fill logos-highlight" } else { "bench-bar-fill" };
4434 rsx! {
4435 div { class: "bench-bar-row",
4436 div { class: "bench-bar-label", "{label}" }
4437 div { class: "bench-bar-track",
4438 div { class: "{bar_class}", style: "width: {pct:.1}%; background: {color};",
4439 if show_inside { span { class: "bench-bar-time", "{time_str}" } }
4440 }
4441 }
4442 if !show_inside { span { class: "bench-bar-time-outside", "{time_str}" } }
4443 }
4444 }
4445 }
4446 }
4447 }
4448 div { class: "bench-note", style: "margin-top:14px;margin-bottom:0;",
4449 "A native binary has no VM to warm up, so the LOGOS interpreter reaches first output in "
4450 strong { "{startup_logos:.1}ms" }
4451 " versus V8\u{2019}s {startup_node:.0}ms, about "
4452 strong { "{startup_vs_v8:.1}x quicker" }
4453 ", which is what matters for short-lived work like cloud functions, CLI tools, and scripts. "
4454 "On long-running loops V8\u{2019}s optimizing JIT pulls ahead: the interpreter is competitive on "
4455 "memory-bound work and behind on heavy compute, a geometric mean of {interp_speed:.2}x the "
4456 "speed of V8 across {interp_offfloor} benchmarks."
4457 }
4458 }
4459
4460 if !engine_size_bars.is_empty() {
4462 div { class: "bench-chart-hint", style: "font-weight:600;color:rgba(229,231,235,0.72);margin-top:22px;",
4463 "Engine size \u{2014} the runtime you ship to execute a program (shorter ships less; stripped, code-only size in parentheses)"
4464 }
4465 div { class: "bench-chart",
4466 for (label, color, bytes, stripped, is_logos) in engine_size_bars.iter() {
4467 {
4468 let pct = (*bytes / engine_size_max * 100.0).min(100.0);
4469 let s = footprint_label(*bytes, *stripped);
4470 let show_inside = pct > 32.0;
4471 let bar_class = if *is_logos { "bench-bar-fill logos-highlight" } else { "bench-bar-fill" };
4472 rsx! {
4473 div { class: "bench-bar-row",
4474 div { class: "bench-bar-label", "{label}" }
4475 div { class: "bench-bar-track",
4476 div { class: "{bar_class}", style: "width: {pct:.1}%; background: {color};",
4477 if show_inside { span { class: "bench-bar-time", "{s}" } }
4478 }
4479 }
4480 if !show_inside { span { class: "bench-bar-time-outside", "{s}" } }
4481 }
4482 }
4483 }
4484 }
4485 }
4486 div { class: "bench-note", style: "margin-top:14px;",
4487 if !wasm_bundle_str.is_empty() {
4488 "In the browser the whole LOGOS engine ships as a "
4489 strong { "{wasm_bundle_str}" }
4490 " WebAssembly bundle \u{2014} the same VM+JIT, no native install. "
4491 }
4492 "Node\u{2019}s binary bundles V8, libuv, and ICU; largo bundles the transpiler, bytecode VM, and copy-and-patch JIT \u{2014} each is the whole engine you ship to run a program. As-built is the real file; stripped removes debug symbols."
4493 }
4494 }
4495
4496 div { class: "bench-chart-hint", style: "font-weight:600;color:rgba(229,231,235,0.72);margin-top:22px;",
4497 "{bench.name} (engine: {interp_engine})"
4498 }
4499
4500 if bench.id == "ackermann" {
4501 div { class: "bench-callout",
4502 span { class: "bench-callout-icon", "\u{26a1}" }
4503 span { "Interpreted recursion is capped at the shared MAX_CALL_DEPTH (2,500 frames); ackermann\u{2019}s deep self-recursion blows past it, so the interpreter runs it at a reduced m. Deep recursion only completes in compiled mode, where the optimizer collapses it." }
4504 }
4505 }
4506
4507 if interp_bars.is_empty() {
4508 div { class: "bench-chart-hint", "No interpreter result for {bench.name} (skipped, or not yet supported by the interpreter)." }
4509 } else {
4510 div { class: "bench-chart-hint",
4511 "Wall-clock time at n = {interp_n} \u{2014} shorter is faster."
4512 if let Some(s) = interp_active_speed {
4513 " The interpreter runs at {s:.2}x the speed of V8 here."
4514 }
4515 }
4516 div { class: "bench-chart",
4517 for (label, color, median, is_logos, _floored) in interp_bars.iter() {
4518 {
4519 let pct = (*median / interp_max * 100.0).min(100.0);
4520 let time_str = format_time(*median);
4521 let show_inside = pct > 15.0;
4522 let bar_class = if *is_logos { "bench-bar-fill logos-highlight" } else { "bench-bar-fill" };
4523 rsx! {
4524 div { class: "bench-bar-row",
4525 div { class: "bench-bar-label", "{label}" }
4526 div { class: "bench-bar-track",
4527 div {
4528 class: "{bar_class}",
4529 style: "width: {pct:.1}%; background: {color};",
4530 if show_inside {
4531 span { class: "bench-bar-time", "{time_str}" }
4532 }
4533 }
4534 }
4535 if !show_inside {
4536 span { class: "bench-bar-time-outside", "{time_str}" }
4537 }
4538 }
4539 }
4540 }
4541 }
4542 }
4543 }
4544 }
4545
4546 div { class: "bench-section", id: "source",
4548 div { class: "bench-section-title", "Source Code" }
4549 div { class: "bench-section-desc",
4550 "The LOGOS source and the Rust it compiles to. Switch an optimization off and watch its \u{201c}## No <X>\u{201d} decorator appear on the LOGOS source — and the generated Rust recompile live in your browser. With every optimization on you see the cached release build; disabling them all yields plain, boring Rust."
4551 }
4552
4553 div { class: "bench-opt-master",
4556 label {
4557 class: if all_opts_on { "bench-opt-toggle on" } else { "bench-opt-toggle off" },
4558 input {
4559 r#type: "checkbox",
4560 checked: all_opts_on,
4561 onchange: move |_| {
4562 let n = logicaffeine_compile::optimization::REGISTRY.len();
4563 let v = if opt_toggles().iter().all(|&b| b) { vec![false; n] } else { vec![true; n] };
4564 opt_toggles.set(v);
4565 live_rust.set(None);
4566 },
4567 }
4568 span { "All Optimizations" }
4569 }
4570 span { class: "bench-opt-hint",
4571 if base_fired().is_empty() {
4572 "analyzing which optimizations this program uses\u{2026}"
4573 } else {
4574 "{base_fired().len()} of {logicaffeine_compile::optimization::REGISTRY.len()} optimizations fire for this program"
4575 }
4576 }
4577 }
4578
4579 div { class: "bench-opt-toggles",
4589 for (node, visible) in opt_tree.iter().zip(tree_visible.iter().copied()) {
4590 if visible {
4591 {
4592 use logicaffeine_compile::optimization::OptRole;
4593 let opt = node.opt;
4594 let ri = opt as usize;
4595 let m = &logicaffeine_compile::optimization::REGISTRY[ri];
4596 let firing = fired_now().contains(&m.keyword);
4597 let needs = node.requires.iter().map(|o| o.meta().keyword).collect::<Vec<_>>().join(", ");
4598 let depends = node.depends_on.iter().map(|o| o.meta().keyword).collect::<Vec<_>>().join(", ");
4599 let blocks = node.preempts.iter().map(|o| o.meta().keyword).collect::<Vec<_>>().join(", ");
4600 let blocked_by = node.preempted_by.iter().map(|o| o.meta().keyword).collect::<Vec<_>>().join(", ");
4601 let blocks_now = preempted_now().iter()
4602 .filter(|(w, _)| *w == m.keyword)
4603 .map(|(_, l)| *l)
4604 .collect::<Vec<_>>()
4605 .join(", ");
4606 let cls = if !opt_tog[ri] { "bench-opt-toggle off" }
4607 else if firing { "bench-opt-toggle on firing" }
4608 else { match node.role {
4609 OptRole::Preempted => "bench-opt-toggle on preempted",
4610 OptRole::Enabler => "bench-opt-toggle on enabling",
4611 OptRole::Fired => "bench-opt-toggle on",
4612 } };
4613 let row_style = format!("padding-left: {}px;", node.depth * 22);
4614 let has_children = node.has_children;
4615 let is_expanded = expanded().get(ri).copied().unwrap_or(true);
4616 let chevron_cls = if is_expanded { "bench-tree-chevron open" } else { "bench-tree-chevron" };
4617 rsx! {
4618 div { class: "bench-tree-row", style: "{row_style}",
4619 if has_children {
4620 span {
4621 class: "{chevron_cls}",
4622 onclick: move |_| {
4623 let mut e = expanded();
4624 if ri < e.len() { e[ri] = !e[ri]; }
4625 expanded.set(e);
4626 },
4627 "\u{25b6}"
4628 }
4629 } else {
4630 span { class: "bench-tree-spacer" }
4631 }
4632 label { class: "{cls}", title: "{m.group}",
4633 input { r#type: "checkbox", checked: opt_tog[ri],
4634 onchange: move |_| {
4635 let toggles = opt_toggles();
4636 let turning_on = !toggles.get(ri).copied().unwrap_or(false);
4637 let mut cfg = config_from_toggles(&toggles);
4638 if turning_on {
4639 cfg.enable_with_requires(opt);
4640 } else {
4641 cfg.set(opt, false);
4642 }
4643 cfg.normalize();
4644 opt_toggles.set(toggles_from_config(&cfg));
4645 live_rust.set(None);
4646 },
4647 }
4648 span { "{m.label}" }
4649 if node.role == OptRole::Enabler {
4650 span { class: "bench-opt-rel enables", "enabler" }
4651 }
4652 if node.role == OptRole::Preempted && !blocked_by.is_empty() {
4653 span { class: "bench-opt-rel beaten", "blocked by {blocked_by} \u{00b7} fires if off" }
4654 }
4655 if !needs.is_empty() {
4656 span { class: "bench-opt-rel needs", "needs {needs}" }
4657 }
4658 if !depends.is_empty() {
4659 span { class: "bench-opt-rel depends", "depends on {depends}" }
4660 }
4661 if !blocks.is_empty() {
4662 span { class: "bench-opt-rel beats", "blocks {blocks}" }
4663 }
4664 if !blocks_now.is_empty() {
4665 span { class: "bench-opt-rel beats-now", "blocks {blocks_now} now" }
4666 }
4667 }
4668 }
4669 }
4670 }
4671 }
4672 }
4673 }
4674
4675 div { class: "bench-source",
4679 div { class: "bench-source-panel",
4680 div { class: "bench-source-header logos", "LOGOS" }
4681 div { class: "bench-source-code", "{opt_decorated}" }
4682 }
4683 div { class: "bench-source-panel",
4684 div { class: "bench-source-header rust", "Generated Rust" }
4685 if rust_loading {
4686 div { class: "bench-source-code",
4687 span { class: "bench-compiling", "Compiling\u{2026}" }
4688 }
4689 } else {
4690 div { class: "bench-source-code", "{rust_text}" }
4691 }
4692 }
4693 }
4694
4695 for (idx, lang_id) in source_langs.iter().enumerate() {
4697 {
4698 let src = get_source(bench_sources, lang_id);
4699 let color = lang_color(lang_id);
4700 let label = lang_label(lang_id);
4701 let version = data.metadata.versions.get(*lang_id).map(|s| s.as_str()).unwrap_or("");
4702 let ext = lang_ext(lang_id);
4703 let file_url = format!("{}/blob/main/benchmarks/programs/{}/{}", GITHUB_REPO, bench.id, ext);
4704 let is_open = source_open()[idx];
4705 rsx! {
4706 div { class: "bench-lang-collapsible",
4707 div {
4708 class: "bench-lang-header",
4709 onclick: move |_| {
4710 let mut arr = source_open();
4711 arr[idx] = !arr[idx];
4712 source_open.set(arr);
4713 },
4714 span {
4715 class: "bench-lang-dot",
4716 style: "background: {color};",
4717 }
4718 span { class: "bench-lang-name", "{label}" }
4719 span { class: "bench-lang-version", "{version}" }
4720 a {
4721 class: "bench-lang-link",
4722 href: "{file_url}",
4723 target: "_blank",
4724 onclick: move |e: Event<MouseData>| e.stop_propagation(),
4725 "View on GitHub \u{2192}"
4726 }
4727 span {
4728 class: if is_open { "bench-collapsible-chevron open" } else { "bench-collapsible-chevron" },
4729 "\u{25b6}"
4730 }
4731 }
4732 div {
4733 class: if is_open { "bench-lang-code open" } else { "bench-lang-code" },
4734 "{src}"
4735 }
4736 }
4737 }
4738 }
4739 }
4740 }
4741
4742 div { class: "bench-section", id: "compilation",
4744 div { class: "bench-section-title", "Compilation Times" }
4745 div { class: "bench-section-desc",
4746 "Time to compile each benchmark from source, at the same flags used for the runtime numbers. "
4747 "LOGOS compiles English to Rust, then invokes rustc at full optimization (-O3, fat LTO, "
4748 "target-cpu=native) \u{2014} so its bar includes the Rust compile."
4749 }
4750
4751 div { class: "bench-chart",
4752 for (name, mean, _stddev, is_largo) in compile_entries.iter() {
4753 {
4754 let pct = (*mean / compile_max * 100.0).min(100.0);
4755 let time_str = format_time(*mean);
4756 let show_inside = pct > 15.0;
4757 let bar_class = if *is_largo { "bench-bar-fill logos-highlight" } else { "bench-bar-fill" };
4758 let color = if *is_largo { "#00d4ff" } else { "#6b7280" };
4759 let display_name = compiler_label(name);
4760 rsx! {
4761 div { class: "bench-bar-row",
4762 div { class: "bench-bar-label", "{display_name}" }
4763 div { class: "bench-bar-track",
4764 div {
4765 class: "{bar_class}",
4766 style: "width: {pct:.1}%; background: {color};",
4767 if show_inside {
4768 span { class: "bench-bar-time", "{time_str}" }
4769 }
4770 }
4771 }
4772 if !show_inside {
4773 span { class: "bench-bar-time-outside", "{time_str}" }
4774 }
4775 }
4776 }
4777 }
4778 }
4779 }
4780
4781 button {
4783 class: "bench-collapsible-btn",
4784 onclick: move |_| compile_detail_open.set(!compile_detail_open()),
4785 span {
4786 class: if compile_detail_open() { "bench-collapsible-chevron open" } else { "bench-collapsible-chevron" },
4787 "\u{25b6}"
4788 }
4789 "Detailed Compilation Data"
4790 }
4791 div {
4792 class: if compile_detail_open() { "bench-collapsible-body open" } else { "bench-collapsible-body" },
4793 table { class: "bench-compile-table",
4794 thead {
4795 tr {
4796 th { "Compiler" }
4797 th { "Mean" }
4798 th { "StdDev" }
4799 }
4800 }
4801 tbody {
4802 for (name, mean, stddev, is_largo) in compile_entries.iter() {
4803 tr {
4804 class: if *is_largo { "highlight" } else { "" },
4805 td { class: "compiler-name", "{compiler_label(name)}" }
4806 td { "{format_time(*mean)}" }
4807 td { "\u{00b1}{format_time(*stddev)}" }
4808 }
4809 }
4810 }
4811 }
4812 }
4813 }
4814
4815 div { class: "bench-section", id: "summary",
4817 div { class: "bench-section-title", "Cross-Benchmark Summary" }
4818 div { class: "bench-section-desc", "Geometric-mean speed vs C across all {data.benchmarks.len()} benchmarks (bars proportional to speed; higher is faster)." }
4819
4820 div { class: "bench-chart",
4821 for (label, val, color, is_logos) in summary_entries.iter() {
4822 {
4823 let pct = linear_bar_pct(*val, summary_max);
4826
4827 let display = if *val >= 0.01 {
4828 format!("{:.2}x", val)
4829 } else {
4830 format!("{:.4}x", val)
4831 };
4832 let show_inside = pct > 20.0;
4833 let bar_class = if *is_logos { "bench-bar-fill logos-highlight" } else { "bench-bar-fill" };
4834 rsx! {
4835 div { class: "bench-bar-row",
4836 div { class: "bench-bar-label", "{label}" }
4837 div { class: "bench-bar-track",
4838 div {
4839 class: "{bar_class}",
4840 style: "width: {pct:.1}%; background: {color};",
4841 if show_inside {
4842 span { class: "bench-bar-time", "{display}" }
4843 }
4844 }
4845 }
4846 if !show_inside {
4847 span { class: "bench-bar-time-outside", "{display}" }
4848 }
4849 }
4850 }
4851 }
4852 }
4853 }
4854 }
4855
4856 div { class: "bench-section", id: "methodology",
4858 button {
4859 class: "bench-collapsible-btn",
4860 style: "margin-top: 0; border-top: none; padding-top: 0;",
4861 onclick: move |_| methodology_open.set(!methodology_open()),
4862 span {
4863 class: if methodology_open() { "bench-collapsible-chevron open" } else { "bench-collapsible-chevron" },
4864 "\u{25b6}"
4865 }
4866 span { style: "font-size: 18px; font-weight: 700; color: #fff;",
4867 "Methodology"
4868 }
4869 }
4870 div {
4871 class: if methodology_open() { "bench-collapsible-body open" } else { "bench-collapsible-body" },
4872 div { class: "bench-methodology",
4873 ul {
4874 li { "Each benchmark measured with hyperfine ({data.metadata.runs.unwrap_or(20)} runs, {data.metadata.warmup.unwrap_or(5)} warmup); the bars show the median." }
4875 li { "CPU: {data.metadata.cpu}." }
4876 li { "OS: {data.metadata.os}." }
4877 li { "Every implementation runs the same algorithm and produces identical, verified output." }
4878 li { "All compiled languages are built at full, matched optimization (see flags below) \u{2014} no language is handicapped relative to LOGOS." }
4879 li { "Two geometric means are reported vs C: the headline keeps all {data.benchmarks.len()} benchmarks; the apples-to-apples figure removes the {collapse_count} where the LOGOS compiler collapses the algorithm (\u{26a1}). Every collapse is auditable in the generated Rust per benchmark." }
4880 li { "The Interpreter-vs-V8 section is a separate peer comparison (LOGOS bytecode VM + JIT against Node/V8) at interpreter-calibrated sizes, so its n differs from the compiled section." }
4881 }
4882
4883 h3 { "Compiler Versions" }
4884 table { class: "bench-version-table",
4885 thead {
4886 tr {
4887 th { "Language" }
4888 th { "Version" }
4889 }
4890 }
4891 tbody {
4892 for (lang_id, version) in data.metadata.versions.iter() {
4893 tr {
4894 td { "{lang_label(lang_id)}" }
4895 td { "{version}" }
4896 }
4897 }
4898 }
4899 }
4900
4901 h3 { "Compiler Flags" }
4902 table { class: "bench-version-table",
4903 thead {
4904 tr {
4905 th { "Language" }
4906 th { "Flags" }
4907 }
4908 }
4909 tbody {
4910 tr { td { "C" } td { "gcc -O3 -march=native -flto -lm" } }
4911 tr { td { "C++" } td { "g++ -O3 -march=native -flto -std=c++17" } }
4912 tr { td { "Rust" } td { "rustc --edition 2021 -C opt-level=3 -C lto=fat -C codegen-units=1 -C target-cpu=native" } }
4913 tr { td { "Zig" } td { "zig build-exe -O ReleaseFast -mcpu native" } }
4914 tr { td { "Go" } td { "go build (Go has no -O levels; this is its optimizing release build)" } }
4915 tr { td { "Java" } td { "javac, run on the HotSpot JIT" } }
4916 tr { td { "Nim" } td { "nim c -d:release --passC:\"-O3 -march=native\"" } }
4917 tr { td { "JavaScript" } td { "node (V8 JIT)" } }
4918 tr { td { "LOGOS" } td { "largo build --release \u{2192} generated Rust \u{2192} rustc -C opt-level=3 -C lto=fat -C codegen-units=1 -C target-cpu=native" } }
4919 tr { td { "LOGOS (interpreted)" } td { "largo run --interpret (bytecode VM + copy-and-patch JIT)" } }
4920 }
4921 }
4922
4923 h3 { "Links" }
4924 ul {
4925 li {
4926 a { href: "{runsh_url}", target: "_blank", "benchmarks/run.sh" }
4927 " \u{2014} benchmark runner script"
4928 }
4929 li {
4930 a { href: "{raw_json_url}", target: "_blank", "results/latest.json" }
4931 " \u{2014} raw benchmark data"
4932 }
4933 li {
4934 a { href: "{history_url}", target: "_blank", "results/history/" }
4935 " \u{2014} historical results by version"
4936 }
4937 li {
4938 a { href: "{bench_dir_url}", target: "_blank", "benchmarks/" }
4939 " \u{2014} all benchmark source code"
4940 }
4941 }
4942 }
4943 }
4944 }
4945 }
4946 }
4947 if bench_tab() == BenchTab::Solver {
4948 div { class: "bench-content",
4949 SolverSection {}
4950 }
4951 }
4952 if bench_tab() == BenchTab::Codec {
4953 div { class: "bench-content",
4954 CodecSection {}
4955 }
4956 }
4957
4958 Footer {}
4959 }
4960 }
4961}