Every number comes from axle bench: run time, CPU time, peak RAM at compile and run, and binary size — with the exact source for each language.
Benchmarks for v0.8.6
StringBuilder append throughput — building one large string.
| Variant | Run | Compile | Output | |||
|---|---|---|---|---|---|---|
| Run time | CPU time | RAM | Compile time | RAM | Binary | |
| Axle -O0 | 5 ms | 5 ms | 29.7 MB | 84 ms | 117.4 MB | 330 KB |
| Axle -O1 | 5 ms | 5 ms | 29.7 MB | 81 ms | 119.9 MB | 330 KB |
| Axle -O2 | 5 ms | 5 ms | 29.7 MB | 91 ms | 117.8 MB | 329 KB |
| Axle -O3 | 5 ms | 4 ms | 29.7 MB | 91 ms | 119.1 MB | 329 KB |
| Rust -O3 | 5 ms | 5 ms | 29.7 MB | 124 ms | 145.8 MB | 3788 KB |
| Rust -O2 | 6 ms | 6 ms | 29.7 MB | 126 ms | 144.4 MB | 3788 KB |
| Rust -O1 | 8 ms | 8 ms | 29.7 MB | 107 ms | 140.3 MB | 3788 KB |
| C++ -O2 | 13 ms | 13 ms | 34.9 MB | 499 ms | 120.8 MB | 17 KB |
| Rust -O0 | 13 ms | 13 ms | 29.7 MB | 101 ms | 141.7 MB | 3791 KB |
| C++ -O3 | 14 ms | 14 ms | 34.7 MB | 519 ms | 120.9 MB | 17 KB |
| C++ -O1 | 15 ms | 15 ms | 35.0 MB | 482 ms | 120.3 MB | 17 KB |
| C++ -O0 | 51 ms | 51 ms | 34.9 MB | 470 ms | 117.0 MB | 30 KB |
// EXPECTED_EXIT: 0
// StringBuilder benchmark #1 — raw append throughput.
// Shared logic with append.cpp / append.rs
// (run via `axle bench --compare cpp,rust`).
//
// Workload: append a fixed 16-byte ASCII chunk 1,000,000 times,
// building a 16 MB buffer (~20 capacity-doubling reallocations).
// Output: total length + 3 sampled bytes (start / middle / end) so
// the three languages cross-check byte-identically without an
// O(n^2) full-string walk.
use std::text::StringBuilder;
fn main() : i32 {
let n : i64 = 1000000;
let chunk : string = "abcdefghijklmnop"; // 16 ASCII bytes
let sb : StringBuilder = new StringBuilder();
let i : i64 = 0;
while (i < n) {
sb.append(chunk);
i = i + 1;
}
// O(1) byte length as the cross-impl correctness signal (the
// workload is deterministic, so identical length ⇒ identical
// bytes). Reading individual bytes back via `byteAt` would be
// O(n) per call (AxleString carries no length → `strlen`), which
// would measure the string ABI, not the builder.
Console::println(sb.byteLength());
sb.dispose();
return 0;
}StringBuilder grow / reset churn across many small builders.
| Variant | Run | Compile | Output | |||
|---|---|---|---|---|---|---|
| Run time | CPU time | RAM | Compile time | RAM | Binary | |
| Rust -O2 | 7 ms | 7 ms | 29.2 MB | 117 ms | 150.2 MB | 3789 KB |
| Rust -O3 | 8 ms | 8 ms | 29.2 MB | 120 ms | 150.2 MB | 3789 KB |
| Axle -O2 | 9 ms | 9 ms | 29.2 MB | 122 ms | 124.1 MB | 355 KB |
| Axle -O0 | 10 ms | 10 ms | 29.2 MB | 109 ms | 125.4 MB | 355 KB |
| Axle -O1 | 10 ms | 10 ms | 29.2 MB | 110 ms | 126.2 MB | 355 KB |
| Axle -O3 | 10 ms | 10 ms | 29.2 MB | 122 ms | 127.1 MB | 355 KB |
| C++ -O2 | 20 ms | 20 ms | 29.2 MB | 490 ms | 120.8 MB | 17 KB |
| C++ -O1 | 21 ms | 21 ms | 29.2 MB | 488 ms | 120.5 MB | 17 KB |
| C++ -O3 | 26 ms | 26 ms | 29.2 MB | 482 ms | 120.7 MB | 17 KB |
| Rust -O1 | 27 ms | 27 ms | 29.2 MB | 106 ms | 146.3 MB | 3789 KB |
| Rust -O0 | 49 ms | 49 ms | 29.2 MB | 102 ms | 147.3 MB | 3795 KB |
| C++ -O0 | 200 ms | 199 ms | 29.2 MB | 508 ms | 117.1 MB | 32 KB |
// EXPECTED_EXIT: 0
// StringBuilder benchmark #3 — build / materialize / reset churn.
// Shared logic with churn.cpp / churn.rs (run via `axle bench --compare cpp,rust`).
//
// Workload: 200,000 outer rounds; each appends a 3-byte chunk 20
// times, materialises the result to a `string` (one alloc + copy),
// reads its first byte into an accumulator (so the build can't be
// optimised away), then clear()s for reuse. Stresses the
// append + toString + clear hot loop with buffer reuse. Output:
// the accumulated first-byte sum.
use std::text::StringBuilder;
fn main() : i32 {
let outer : i64 = 200000;
let inner : i64 = 20;
let chunk : string = "xyz";
let sb : StringBuilder = new StringBuilder();
let acc : i64 = 0;
let o : i64 = 0;
while (o < outer) {
let k : i64 = 0;
while (k < inner) {
sb.append(chunk);
k = k + 1;
}
let s : string = sb.toString();
acc = acc + (s.byteAt(0) as i64);
sb.clear();
o = o + 1;
}
Console::println(acc);
sb.dispose();
return 0;
}Integer-to-string formatting via StringBuilder.
| Variant | Run | Compile | Output | |||
|---|---|---|---|---|---|---|
| Run time | CPU time | RAM | Compile time | RAM | Binary | |
| Axle -O1 | 8 ms | 8 ms | 29.5 MB | 91 ms | 117.7 MB | 330 KB |
| Axle -O0 | 9 ms | 9 ms | 29.5 MB | 93 ms | 117.8 MB | 330 KB |
| Axle -O2 | 9 ms | 9 ms | 29.5 MB | 102 ms | 119.9 MB | 330 KB |
| Axle -O3 | 9 ms | 9 ms | 29.5 MB | 111 ms | 120.4 MB | 330 KB |
| C++ -O2 | 9 ms | 9 ms | 29.5 MB | 491 ms | 121.0 MB | 17 KB |
| C++ -O3 | 9 ms | 9 ms | 29.5 MB | 499 ms | 120.9 MB | 17 KB |
| C++ -O1 | 10 ms | 10 ms | 29.5 MB | 489 ms | 120.7 MB | 17 KB |
| Rust -O3 | 13 ms | 13 ms | 29.5 MB | 128 ms | 145.0 MB | 3789 KB |
| Rust -O2 | 16 ms | 16 ms | 29.5 MB | 126 ms | 147.0 MB | 3789 KB |
| Rust -O1 | 18 ms | 18 ms | 29.5 MB | 109 ms | 144.8 MB | 3789 KB |
| Rust -O0 | 43 ms | 43 ms | 29.5 MB | 99 ms | 147.9 MB | 3798 KB |
| C++ -O0 | 103 ms | 103 ms | 29.5 MB | 479 ms | 117.4 MB | 30 KB |
// EXPECTED_EXIT: 0
// StringBuilder benchmark #2 — integer formatting throughput.
// Shared logic with int_format.cpp / int_format.rs
// (run via `axle bench --compare cpp,rust`).
//
// Workload: append 500,000 LCG-driven integers (each formatted to
// decimal) separated by commas — a CSV-style builder. Stresses
// `appendI64` (int→string conversion + bulk append) plus the
// comma separator append. Output: total length + first / last byte.
//
// Caveat: `appendI64` formats digits directly into the builder's
// buffer, so it pays no separate string allocation for the formatted
// number. `int_format.cpp`'s `std::to_string` and `int_format.rs`'s
// `to_string()` each materialise a temporary `std::string` / `String`
// per integer before appending it — real work this bench does not ask
// Axle to do, not a language-speed result.
use std::text::StringBuilder;
fn main() : i32 {
let n : i64 = 500000;
let sb : StringBuilder = new StringBuilder();
let seed : i64 = 12345;
let i : i64 = 0;
while (i < n) {
seed = (seed * 48271) % 2147483647;
sb.appendI64(seed % 100000);
sb.append(",");
i = i + 1;
}
// O(1) byte length as the cross-impl correctness signal (same
// LCG + same integer formatting ⇒ identical bytes ⇒ identical
// length). See append.axle for why per-byte readback is avoided.
Console::println(sb.byteLength());
sb.dispose();
return 0;
}