← Back to Architecture & Comparison Guides
Published: 2026-10-23 Updated: 2026-10-23 Source: bend2.org

Bend 2 vs. C: Single-Core Parity, Multi-Core/GPU Scaling, and Why Quadtree Matrix Multiply Beats Naive C

How Bend 2 compiles to a single C11 file (bend -o prog.c), how it compares to clang -O3 across all 16 Apple M4 Max benchmarks, and when to choose C vs. Bend 2.


Executive Summary: Bottom Line Up Front

Bend 2 does not replace C; it compiles directly to a single, self-contained C file (bend prog.bend -o prog.c) (bendlang/bend). That emitted file serves simultaneously as a single-core CPU binary, a 16-core pthread runner, and an Apple Metal or NVIDIA CUDA GPU kernel (bendlang/bend).

  1. Choose C (clang -O3 / gcc -O3) for operating system kernels, device drivers, embedded microcontrollers, and algorithms dominated by in-place mutation over flat contiguous byte buffers. On flat hash tables (hashmap: 0.622 s C vs. 2.741 s Bend SEQ-CPU) and contiguous byte-stream tokenizers (lexer: 1.036 s C vs. 2.144 s Bend SEQ-CPU), single-core C beats single-core Bend by 2.07x to 4.41x (benchmark dataset).
  2. Choose Bend 2 (bend -o) when your algorithm recursively decomposes trees, spatial grids, graphs, or sorting networks and you want multi-core CPU (PAR-CPU) and GPU (PAR-GPU) scaling without writing pthread_create, atomics, or CUDA shaders. Across 14 of the 16 official Apple M4 Max benchmarks, single-core Bend (SEQ-CPU) runs within 0.70x to 1.50x of hand-written -O3 C, actually beating C on quadtree matrix multiplication (2.064 s vs. 2.964 s). Switching on PAR-CPU (16 cores) and PAR-GPU (Metal) accelerates the exact same source file up to 107.6x faster than sequential C (0.063 s vs. 6.776 s on Conway’s Game of Life) (benchmark dataset).

1. How bend -o prog.c Works Under the Hood

Legacy functional runtimes (including Bend 1 / HVM2) suffered on single-core CPUs because they represented every function call as a heap-allocated interaction-net node (HigherOrderCO/HVM). Bend 2’s BendRT compiler (bend2/comp.ts) eliminates graph-node interpretation in favor of four low-level C codegen techniques (bendlang/bend):

  1. Zero C Call Stack (musttail Worklist Machine): Bend functions do not push C stack frames (call/ret). Instead, every function compiles to a segment inside a flat worklist loop (WL_CASE(FID_...)) using musttail jumps with register-pinned state.
  2. Inline 64-Bit Term Packing: Every Bend value is a 64-bit word ([rfc(1) | tag(7) | aux(16) | loc(40)]). Machine scalars (U32, F32), natural numbers below 248, and unary constructors (Leaf{x}) are packed directly into the 64-bit word with zero malloc calls and zero heap bytes (benchmark dataset).
  3. Whole-Program Share & Borrow Inference: When a + variable is only read across recursive calls, BendRT’s borrow inference passes raw 64-bit pointers without bumping reference counts.
  4. Instant Affine Deallocation (heap_free): Because affine variables are consumed at most once, pattern matching (case Node{l, r}:) immediately returns its 2-word slot to a thread-local freelist (heap_free). Resident memory stays between 2.0 MB and 2.8 MB RSS on 12 of the 16 M4 Max benchmarks (benchmark dataset).

2. Complete Apple M4 Max Benchmark Suite: Hand-Written C (clang -O3) vs. Bend 2

The table below reports all 16 workloads from the pinned Apple M4 Max benchmark suite (/data/bend-ecosystem.json), comparing hand-written sequential C (c_sec) against Bend 2 SEQ-CPU, 16-core PAR-CPU, and Metal PAR-GPU (benchmark dataset):

Workload Category C (clang -O3) Bend SEQ-CPU (RSS) SEQ / C Ratio Bend PAR-CPU 16c Bend PAR-GPU Metal PAR-GPU vs. C
Tree Matrix Multiply Quadtree Algebra 2.964 s 2.064 s (2.8 MB) 0.70x (Bend faster!) 0.226 s 0.231 s 12.83x faster (source)
K-Means Clustering Machine Learning 2.062 s 2.069 s (23.5 MB) 1.00x (Tie) 0.268 s 0.191 s 10.80x faster (source)
Procedural Terrain Simplex Noise 2.198 s 2.225 s (2.1 MB) 1.01x 0.197 s 0.106 s 20.74x faster (source)
Tree Bitonic Sort Sorting Networks 5.719 s 6.043 s (146 MB) 1.06x 0.799 s 0.450 s 12.71x faster (source)
Breadth-First Search Graph Traversal 3.585 s 3.918 s (2.0 MB) 1.09x 0.343 s 0.207 s 17.32x faster (source)
N-Body Physics Particle Gravity 5.126 s 5.758 s (2.2 MB) 1.12x 0.516 s 0.059 s 86.88x faster (source)
Conway’s Game of Life Cellular Automata 6.776 s 7.803 s (2.1 MB) 1.15x 0.647 s 0.063 s 107.56x faster (source)
Mandelbrot Fractal Complex Plane 3.750 s 4.473 s (2.3 MB) 1.19x 0.395 s 0.057 s 65.79x faster (source)
Merkle Tree Cryptographic Trees 3.423 s 4.222 s (130 MB) 1.23x 0.422 s 0.071 s 48.21x faster (source)
3D Raytracer Computer Graphics 3.646 s 4.616 s (2.1 MB) 1.27x 0.396 s 0.151 s 24.15x faster (source)
Levenshtein Edit Dist Dynamic Prog. 2.002 s 2.559 s (2.0 MB) 1.28x 0.236 s 0.144 s 13.90x faster (source)
Symbolic Regression Genetic AST Search 2.208 s 3.014 s (2.1 MB) 1.37x 0.266 s 0.529 s 8.30x (PAR-CPU) (source)
Parallel Radix Sort Sorting Networks 2.791 s 4.083 s (651 MB) 1.46x 0.445 s 0.314 s 8.89x faster (source)
N-Queens Solver Combinatorics 3.599 s 5.406 s (2.0 MB) 1.50x 0.455 s 0.933 s 7.91x (PAR-CPU) (source)
Regex Parallel Lexer Text Processing 1.036 s 2.144 s (2.0 MB) 2.07x 0.198 s 1.075 s 5.23x (PAR-CPU) (source)
HAMT Hashmap Data Structures 0.622 s 2.741 s (2.4 MB) 4.41x 0.238 s 0.521 s 2.61x (PAR-CPU) (source)

3. Architectural Teardown: Two Surprising Benchmark Outcomes

3.1 Why Single-Core Bend 2 Beats Hand-Written C on tree-matmul (2.064 s vs. 2.964 s)

How can a pure functional language beat clang -O3 C on single-threaded quadtree matrix multiplication (benchmark dataset)?

  1. Packed Leaf Words vs. Tagged C Struct Pointers: In Bend 2, Leaf{v} packs its scalar payload inside the 64-bit word (term_aux(t) == CID_LEAF), returning immediately from registers without a memory load.
  2. Worklist Jumps vs. 8-Way C Call Prologue/Epilogue: Multiplying two 2 × 2 quadtree blocks performs 8 recursive multiplications and 4 recursive additions. In C, each recursive call spills registers and allocates a C stack frame; in BendRT, tail calls reload registers in place (WL_AGAIN) inside a single worklist function (bendlang/bend).
  3. Zero-Cost Borrow Inference: BendRT proves statically that input matrix subtrees are read-only during multiplication, passing borrowed 64-bit words with zero reference-count traffic.

3.2 Why Single-Core C Beats Single-Core Bend 2 on hashmap (0.622 s vs. 2.741 s) and lexer (1.036 s vs. 2.144 s)

Conversely, C’s raw pointer model wins on flat, sequential memory layouts (benchmark dataset):


4. Embedding Bend 2 Inside C Applications via Foreign Effects & Shared Libraries

Because bend prog.bend -o prog.c outputs standard ISO C11, C systems can embed verified Bend 2 modules directly (bendlang/bend):

# 1. Verify all inductive laws in 0.38 s
bend kernel_proof.bend --verdict

# 2. Emit standalone C11 code and compile as a shared library
bend kernel.bend -o kernel.c
clang -O3 -shared -fPIC -pthread kernel.c -o libbend_kernel.so

Bend 2’s foreign effect system (import "./io_shim.c") compiles any def custom_effect(x: U32) -> IO(U32) declaration into a direct C function call at the sequential IO loop boundary. This gives C systems engineers a clean division of labor: keep low-level socket buffers, mmap file I/O, and hardware drivers in C, and offload parallel tree/graph kernels and LAWS.bend-verified state machines to Bend 2 (benchmark dataset).