Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions BENCHMARKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
108 changes: 76 additions & 32 deletions src/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

let mut out = String::with_capacity(content.len());
Expand All @@ -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());
Expand All @@ -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);
Expand All @@ -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<HistoryRow> {
Expand All @@ -290,12 +300,12 @@ fn parse_history_rows(section: &str) -> Vec<HistoryRow> {
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::<f64>().ok())
{
rows.push(HistoryRow { version, rate });
rows.push(HistoryRow { version, time });
}
}
rows
Expand All @@ -304,15 +314,15 @@ fn parse_history_rows(section: &str) -> Vec<HistoryRow> {
fn upsert_history_row(
mut rows: Vec<HistoryRow>,
version: &str,
rate: f64,
time: f64,
_baseline: f64,
) -> Vec<HistoryRow> {
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
Expand All @@ -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 {
Expand All @@ -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();
}
Expand Down Expand Up @@ -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

Expand All @@ -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"));
}
Expand All @@ -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

Expand All @@ -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]
Expand Down Expand Up @@ -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");
Expand Down
Loading