Benchmarks

Measured against the languages you'd compare to.

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.14.0

Strings

append

StringBuilder append throughput — building one large string.

VariantRunCompileOutput
Run timeCPU timeRAMCompile timeRAMBinary
Rust -O25 ms5 ms45.9 MB104 ms0 KB3863 KB
Rust -O35 ms5 ms45.9 MB105 ms0 KB3863 KB
Rust -O16 ms5 ms45.9 MB100 ms0 KB3863 KB
Axle -O07 ms7 ms45.9 MB70 ms0 KB305 KB
Axle -O17 ms7 ms45.9 MB71 ms0 KB305 KB
Axle -O27 ms7 ms45.9 MB80 ms0 KB305 KB
Axle -O37 ms7 ms45.9 MB79 ms0 KB305 KB
C++ -O112 ms12 ms45.9 MB444 ms0 KB17 KB
C++ -O212 ms12 ms45.9 MB448 ms0 KB17 KB
C++ -O312 ms12 ms45.9 MB456 ms0 KB17 KB
Rust -O034 ms34 ms45.9 MB100 ms0 KB3869 KB
C++ -O051 ms51 ms45.9 MB424 ms0 KB30 KB
▸ Source
// 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.
    println(sb.byteLength());
    sb.dispose();
    return 0;
}

churn

StringBuilder grow / reset churn across many small builders.

VariantRunCompileOutput
Run timeCPU timeRAMCompile timeRAMBinary
Rust -O29 ms9 ms45.9 MB113 ms0 KB3863 KB
Rust -O39 ms9 ms45.9 MB110 ms0 KB3863 KB
Axle -O111 ms11 ms45.9 MB80 ms0 KB312 KB
Rust -O111 ms11 ms45.9 MB103 ms0 KB3863 KB
Axle -O012 ms12 ms45.9 MB78 ms0 KB312 KB
Axle -O212 ms12 ms45.9 MB91 ms0 KB312 KB
Axle -O312 ms12 ms45.9 MB92 ms0 KB312 KB
C++ -O220 ms20 ms45.9 MB455 ms0 KB17 KB
C++ -O121 ms21 ms45.9 MB450 ms0 KB17 KB
C++ -O327 ms27 ms45.9 MB455 ms0 KB17 KB
Rust -O0138 ms138 ms45.9 MB100 ms0 KB3870 KB
C++ -O0193 ms193 ms45.9 MB432 ms0 KB32 KB
▸ Source
// 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;
    }

    println(acc);
    sb.dispose();
    return 0;
}

int_format

Integer-to-string formatting via StringBuilder.

VariantRunCompileOutput
Run timeCPU timeRAMCompile timeRAMBinary
Axle -O39 ms9 ms46.2 MB87 ms0 KB305 KB
C++ -O29 ms9 ms46.2 MB460 ms0 KB17 KB
C++ -O39 ms9 ms46.2 MB462 ms0 KB17 KB
Axle -O010 ms10 ms46.2 MB75 ms0 KB305 KB
Axle -O110 ms9 ms46.2 MB75 ms0 KB305 KB
Axle -O210 ms10 ms46.2 MB89 ms0 KB305 KB
C++ -O110 ms10 ms46.2 MB461 ms0 KB17 KB
Rust -O215 ms15 ms46.2 MB114 ms0 KB3864 KB
Rust -O315 ms15 ms46.2 MB114 ms0 KB3863 KB
Rust -O118 ms18 ms46.2 MB105 ms0 KB3863 KB
Rust -O062 ms61 ms46.2 MB103 ms0 KB3874 KB
C++ -O094 ms94 ms46.2 MB435 ms0 KB30 KB
▸ Source
// 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.
    println(sb.byteLength());
    sb.dispose();
    return 0;
}