Skip to content

Performance ​

CeeSafe compiles through C, with every safety check on: array bounds, integer overflow, lossy conversions. This page measures what that costs against Rust and plain C on sixteen workloads, and says where CeeSafe is ahead or behind and why.

The result ​

Over the sixteen workloads CeeSafe takes 0.96× Rust's time (geometric mean), and 0.88× that of Rust built with overflow checks, which is the comparison that checks the same things CeeSafe does. Hand-written C without any check takes 0.94×.

WorkloadCeeSafeRustRust, overflow checksCCeeSafe (ms)Rust (ms)
heap-alloc0.281.000.920.95121428
nbody0.821.001.080.82500611
stack-alloc0.841.001.700.85244289
vector-growth0.871.001.030.80385443
arena0.951.000.910.98241254
memory-copy0.991.001.061.13595602
linked-list1.011.000.990.90366361
large-objects1.021.000.971.02254250
small-objects1.031.001.000.89510493
string-building1.031.001.310.78299292
numeric1.121.001.001.14240214
binary-trees1.131.001.020.91561494
parsing1.161.001.101.05161139
data-oriented1.181.001.051.00381323
recursion1.261.001.261.00407323
hash-map1.411.001.170.94453321
geometric mean0.961.001.090.94

Times are each language's median wall time over five runs (after one discarded run) divided by Rust's; lower is faster. Measured 2026-10-31 on an Apple M-series Mac (arm64, macOS), with ceesafec 0.2.0-dev, rustc 1.99.1 and Apple clang 21. A run-to-run spread of a few percent is normal: compare ratios, not single milliseconds.

Peak memory is the same as Rust's or lower everywhere (arena: 7.8 MiB against 17.7 MiB). Binaries are about 40–56 KiB against Rust's 460 KiB. ceesafec compiles each program in about a quarter of a second, roughly twice rustc's time on these single-file programs, since it runs the C compiler after its own work.

Where CeeSafe is ahead ​

  • heap-alloc, 3.5× faster than Rust. ceesafec's placement optimisation keeps a box that never escapes its function on the stack, so the program's per-iteration heap array costs no allocation at all. It beats hand-written C, which calls malloc and free each time.
  • stack-alloc, nbody, vector-growth. CeeSafe matches C. Its overflow and bounds checks are removed where ceesafec proves they cannot fail (loop indexes within a length, sums of bounded values), which Rust with overflow checks does not manage on stack-alloc (1.70×).
  • arena. A region's bump allocation is inlined into the program (Decision 0007), and the arena's chunks take less than half the memory of Rust's.

Where CeeSafe is behind, and why ​

  • hash-map (1.41×). Rust builds its tables with vec![0; n], so LLVM knows their length and removes the bounds checks on keys[s & MASK]. A CeeSafe Vector can only be filled by push, so the length is unknown and every probe keeps its check: with the checks compiled out the program runs at Rust's speed. A filled-vector constructor would close this gap.
  • recursion (1.26×) and parsing (1.16×). The overflow check on fib(n - 1) + fib(n - 2) and on accumulating digits cannot be proven unnecessary; Rust pays the same when its overflow checks are on (1.26× and 1.10×).
  • data-oriented (1.18×). As for hash-map, the particle arrays are filled by push; the bounds checks cost about 11%.
  • binary-trees (1.13×) and small-objects (1.03×). Every heap allocation goes through the runtime's ceesafe_alloc, which counts allocations (for CEESAFE_ALLOC_REPORT and fault injection) before calling malloc. Calling malloc directly measured 12% faster on both.

Run it yourself ​

sh
python3 benchmarks/run.py                                    # every workload: cee, rust, rust-checked, c; 5 runs
python3 benchmarks/run.py --workloads hash-map,nbody --runs 3
python3 benchmarks/run.py --cee-flag --no-bce                # with bounds-check elimination off

Each workload is the same algorithm with the same data structures in every language, and prints one checksum, which the runner checks. benchmarks/README.md describes the workloads and the build flags. To see which checks ceesafec kept in a program, build it with --explain-bounds-checks, --explain-overflow-checks or --explain-allocations (The ceesafec command).

Pre-1.0: the language and its tools are still changing. CeeSafe forked from Nova 0.6.0.