diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 8dd3153..e91ed42 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -30,15 +30,15 @@ kangaroo --benchmark ### Performance History -| Version | 48-bit Rate | Improvement | +| Version | 48-bit Time | Improvement | |---------|-------------|-------------| -| v0.2.0 | 3.70 M/s | baseline | -| v0.3.0 | 5.50 M/s | +49% | -| v0.4.0 | 8.84 M/s | +139% | -| v0.6.0 | 17.00 M/s | +359% | -| v0.7.0 | 23.33 M/s | +531% | -| v0.7.1 | 23.27 M/s | +529% | -| v0.8.0 | 21.79 M/s | +489% | +| v0.3.0 | 30.35s | baseline | +| v0.4.0 | 25.11s | +17% | +| v0.5.0 | 25.10s | +17% | +| v0.6.0 | 12.93s | +57% | +| v0.7.0 | 12.91s | +57% | +| v0.7.1 | 12.92s | +57% | +| v0.8.0 | 0.87s | +97% | ## Contributing diff --git a/src/benchmark.rs b/src/benchmark.rs index be621eb..9e99ae0 100644 --- a/src/benchmark.rs +++ b/src/benchmark.rs @@ -30,6 +30,12 @@ const CASES: &[Case] = &[ start: "800000000000", range_bits: 48, }, + Case { + name: "49-bit", + pubkey: "02591d682c3da4a2a698633bf5751738b67c343285ebdc3492645cb44658911484", + start: "1000000000000", + range_bits: 49, + }, ]; struct CaseResult { @@ -166,13 +172,13 @@ fn generate_markdown( out }; - let rate_48 = results + let time_48 = results .iter() .find(|r| r.name == "48-bit") - .map(|r| r.rate / 1_000_000.0); + .map(|r| r.time_secs); - if let Some(rate) = rate_48 { - content = update_performance_history(&content, version, rate); + if let Some(time) = time_48 { + content = update_performance_history(&content, version, time); } content @@ -229,15 +235,19 @@ fn format_ops(ops: u64) -> String { result } -fn update_performance_history(content: &str, version: &str, rate_m: f64) -> String { +fn update_performance_history(content: &str, version: &str, time_secs: f64) -> String { let version_tag = format!("v{version}"); let section_header = "### Performance History"; if let Some(range) = find_gpu_section(content, section_header) { let section = &content[range.0..range.1]; let rows = parse_history_rows(section); - let baseline = rows.first().map(|r| r.rate).unwrap_or(rate_m); - let new_rows = upsert_history_row(rows, &version_tag, rate_m, baseline); + let has_data_rows = section.lines().any(|line| line.starts_with("| v")); + if has_data_rows && rows.is_empty() { + return content.to_string(); + } + let baseline = rows.first().map(|r| r.time).unwrap_or(time_secs); + let new_rows = upsert_history_row(rows, &version_tag, time_secs, baseline); let new_section = format_history_section(&new_rows, baseline); let mut out = String::with_capacity(content.len()); @@ -250,9 +260,9 @@ fn update_performance_history(content: &str, version: &str, rate_m: f64) -> Stri let new_section = format_history_section( &[HistoryRow { version: version_tag, - rate: rate_m, + time: time_secs, }], - rate_m, + time_secs, ); let mut out = String::with_capacity(content.len() + new_section.len()); @@ -264,9 +274,9 @@ fn update_performance_history(content: &str, version: &str, rate_m: f64) -> Stri let new_section = format_history_section( &[HistoryRow { version: version_tag, - rate: rate_m, + time: time_secs, }], - rate_m, + time_secs, ); let mut out = content.to_string(); out.push_str(&new_section); @@ -276,7 +286,7 @@ fn update_performance_history(content: &str, version: &str, rate_m: f64) -> Stri struct HistoryRow { version: String, - rate: f64, + time: f64, } fn parse_history_rows(section: &str) -> Vec { @@ -290,12 +300,12 @@ fn parse_history_rows(section: &str) -> Vec { continue; } let version = cols[1].trim().to_string(); - let rate_str = cols[2].trim(); - if let Some(rate) = rate_str - .strip_suffix(" M/s") + let time_str = cols[2].trim(); + if let Some(time) = time_str + .strip_suffix("s") .and_then(|s| s.trim().parse::().ok()) { - rows.push(HistoryRow { version, rate }); + rows.push(HistoryRow { version, time }); } } rows @@ -304,15 +314,15 @@ fn parse_history_rows(section: &str) -> Vec { fn upsert_history_row( mut rows: Vec, version: &str, - rate: f64, + time: f64, _baseline: f64, ) -> Vec { if let Some(existing) = rows.iter_mut().find(|r| r.version == version) { - existing.rate = rate; + existing.time = time; } else { rows.push(HistoryRow { version: version.to_string(), - rate, + time, }); } rows @@ -322,13 +332,14 @@ fn format_history_section(rows: &[HistoryRow], baseline: f64) -> String { let mut s = String::new(); writeln!(s, "### Performance History").unwrap(); writeln!(s).unwrap(); - writeln!(s, "| Version | 48-bit Rate | Improvement |").unwrap(); + writeln!(s, "| Version | 48-bit Time | Improvement |").unwrap(); writeln!(s, "|---------|-------------|-------------|").unwrap(); for row in rows { - let improvement = if (row.rate - baseline).abs() < 0.01 { + let improvement = if (row.time - baseline).abs() < 0.01 { "baseline".to_string() } else { - let pct = ((row.rate - baseline) / baseline) * 100.0; + // Lower time = better, so invert the percentage + let pct = ((baseline - row.time) / baseline) * 100.0; if pct >= 0.0 { format!("+{pct:.0}%") } else { @@ -337,8 +348,8 @@ fn format_history_section(rows: &[HistoryRow], baseline: f64) -> String { }; writeln!( s, - "| {} | {:.2} M/s | {} |", - row.version, row.rate, improvement + "| {} | {:.2}s | {} |", + row.version, row.time, improvement ) .unwrap(); } @@ -497,10 +508,10 @@ Submit your results! ### Performance History -| Version | 48-bit Rate | Improvement | +| Version | 48-bit Time | Improvement | |---------|-------------|-------------| -| v0.2.0 | 3.70 M/s | baseline | -| v0.5.0 | 8.84 M/s | +139% | +| v0.2.0 | 25.00s | baseline | +| v0.5.0 | 15.00s | +40% | ## Contributing @@ -509,7 +520,7 @@ Submit your results! let content = generate_markdown("Test GPU", "0.6.0", &sample_results(), existing); assert!(content.contains("v0.6.0")); - assert!(content.contains("17.10 M/s")); + assert!(content.contains("13.00s")); assert_eq!(content.matches("v0.2.0").count(), 1); assert!(content.contains("baseline")); } @@ -523,10 +534,10 @@ Submit your results! ### Performance History -| Version | 48-bit Rate | Improvement | +| Version | 48-bit Time | Improvement | |---------|-------------|-------------| -| v0.2.0 | 3.70 M/s | baseline | -| v0.5.0 | 8.84 M/s | +139% | +| v0.2.0 | 25.00s | baseline | +| v0.5.0 | 15.00s | +40% | ## Contributing @@ -536,7 +547,7 @@ Submit your results! let content = generate_markdown("New GPU", "0.5.0", &sample_results(), existing); assert!(content.contains("*Version: 0.5.0*")); assert_eq!(content.matches("v0.5.0").count(), 1); - assert!(content.contains("17.10 M/s")); + assert!(content.contains("13.00s")); } #[test] @@ -591,6 +602,39 @@ Submit your results! assert!(content.contains("1.00 M/s")); } + #[test] + fn preserves_legacy_history_when_parse_fails() { + let existing = "\ +# Benchmark Results + +## Results + +### Test GPU + +| Range | Time | Ops | Rate | +|-------|------|-----|------| +| 48-bit | 10.00s | 100,000,000 | 10.00 M/s | + +*Version: 0.5.0* + +### Performance History + +| Version | 48-bit Rate | Improvement | +|---------|-------------|-------------| +| v0.2.0 | 3.70 M/s | baseline | +| v0.5.0 | 8.84 M/s | +139% | + +## Contributing + +Submit your results! +"; + + let content = generate_markdown("Test GPU", "0.6.0", &sample_results(), existing); + // Legacy M/s rows can't be parsed, so history section must be preserved as-is + assert!(content.contains("3.70 M/s")); + assert!(content.contains("8.84 M/s")); + } + #[test] fn formats_ops_with_separators() { assert_eq!(format_ops(0), "0");