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

Bend 2 vs. CUDA & Metal: Manual GPU Shaders vs. the 128x128 Task Cube and the GPU Bimodality Law

When to write raw CUDA C++ or Metal Shading Language kernels vs. compiling Bend 2 (bend -o), explaining the GPU Bimodality Law across all 16 Apple M4 Max benchmarks.


Executive Summary: Bottom Line Up Front

Bend 2 is not a competitor to NVIDIA CUDA or Apple Metal; it is a high-level affine functional compiler that emits unified C, CUDA, and Metal shader code (bend prog.bend -o prog) (bendlang/bend).

  1. Write raw CUDA C++ (.cu / PTX) or Metal Shading Language (.metal) when your workload is dense contiguous linear algebra (GEMM, FlashAttention, convolution stencils). Raw shaders saturate hardware Tensor Cores (mma.sync / simdgroup_matrix), explicit threadgroup shared memory (__shared__), and warp-shuffle intrinsics (__shfl_sync).
  2. Write Bend 2 (f!(x) GPU dispatch) when your workload is a recursive divide-and-conquer tree, spatial grid, cellular automaton, particle simulation, or BVH raytracer. Instead of flattening pointer trees into index arrays, you write pure recursive functions. On an Apple M4 Max, Bend 2’s Metal backend achieves up to 123.9x GPU speedup (0.063 s on Conway’s Game of Life and 0.059 s on N-Body). It also verifies 3,200 formal proofs in 0.38 s (benchmark dataset).
  3. Respect the GPU Bimodality Law: Our 16-workload Apple M4 Max benchmark suite proves that balanced parallel workloads run up to 123.9x faster on GPU. However, heavily branching irregular workloads (lexer, queens, symreg, hashmap) run 2.0x to 5.4x faster on 16 CPU cores (PAR-CPU) than on the GPU (PAR-GPU) (benchmark dataset).

1. How BendRT Compiles Functional Recursion to CUDA Warps and Metal Threadgroups

In traditional GPU programming, dynamic recursion and heap allocation inside a kernel either fail to compile or exhaust per-thread stack memory. Bend 2 (bend2/comp.ts and Lulzx/bendc) solves this by compiling .bend files into a flat state-machine shader backed by the 128x128 Task Cube (bendlang/bend):


2. The GPU Bimodality Law: All 16 Apple M4 Max Benchmarks (PAR-GPU vs. PAR-CPU)

Because Bend 2 runs the exact same source file on 1 CPU core (SEQ-CPU), 16 CPU cores (PAR-CPU), and Metal GPU warps (PAR-GPU), the M4 Max benchmarks reveal a clear hardware boundary (benchmark dataset):

Workload Category Bend SEQ-CPU Bend PAR-CPU (16c) Bend PAR-GPU (Metal) GPU vs. SEQ Faster Parallel Target (GPU vs. 16c CPU)
Conway’s Game of Life Cellular Automata 7.803 s 0.647 s 0.063 s 123.9x GPU wins by 10.27x (source)
N-Body Physics Particle Gravity 5.758 s 0.516 s 0.059 s 97.6x GPU wins by 8.75x (source)
Mandelbrot Set Complex Plane 4.473 s 0.395 s 0.057 s 78.5x GPU wins by 6.93x (source)
Merkle Tree Cryptographic Trees 4.222 s 0.422 s 0.071 s 59.5x GPU wins by 5.94x (source)
3D Raytracer Computer Graphics 4.616 s 0.396 s 0.151 s 30.6x GPU wins by 2.62x (source)
Procedural Terrain Simplex Noise 2.225 s 0.197 s 0.106 s 21.0x GPU wins by 1.86x (source)
Tree Bitonic Sort Sorting Networks 6.043 s 0.799 s 0.450 s 13.4x GPU wins by 1.78x (source)
Breadth-First Search Graph Traversal 3.918 s 0.343 s 0.207 s 18.9x GPU wins by 1.66x (source)
Levenshtein Edit Dist Wavefront DP 2.559 s 0.236 s 0.144 s 17.8x GPU wins by 1.64x (source)
Parallel Radix Sort Sorting Networks 4.083 s 0.445 s 0.314 s 13.0x GPU wins by 1.42x (source)
K-Means Clustering Machine Learning 2.069 s 0.268 s 0.191 s 10.8x GPU wins by 1.40x (source)
Tree Matrix Multiply Quadtree Algebra 2.064 s 0.226 s 0.231 s 8.9x Tie (0.226 s CPU vs. 0.231 s GPU) (source)
Symbolic Regression Genetic AST Search 3.014 s 0.266 s 0.529 s 5.7x 16c CPU wins by 1.99x (source)
N-Queens Solver Backtracking Search 5.406 s 0.455 s 0.933 s 5.8x 16c CPU wins by 2.05x (source)
HAMT Hashmap Data Structures 2.741 s 0.238 s 0.521 s 5.3x 16c CPU wins by 2.19x (source)
Regex Parallel Lexer Text Tokenization 2.144 s 0.198 s 1.075 s 2.0x 16c CPU wins by 5.43x (source)

Why 16 CPU Cores Beat the GPU on lexer, hashmap, queens, and symreg

  1. SIMT Warp Divergence on Unbalanced Trees (queens, symreg): GPU hardware executes 32 threads in lockstep (a CUDA warp or Metal SIMD-group). In N-Queens backtracking (queens) and genetic AST evaluation (symreg), one lane prunes immediately while a neighboring lane in the same warp recurses 20 levels deeper, forcing the other 31 lanes to stall. Apple M4 Max’s 16 independent out-of-order CPU cores suffer zero warp divergence (benchmark dataset).
  2. Pointer-Chasing Latency vs. Deep CPU Caches (hashmap, lexer): Traversing linked lists of characters (lexer) or trie nodes (hashmap) is memory-latency bound rather than compute bound, favoring the CPU’s low-latency L1/L2 caches (0.198 s PAR-CPU vs. 1.075 s PAR-GPU on lexer). Because Bend 2 lets you toggle CPU vs. GPU execution with a single ! annotation (or runtime flag), you can benchmark both targets in seconds without rewriting a single line of algorithm code (bendlang/bend).

3. Code Comparison: Flattened CUDA Array Indexing vs. Bend 2 f!(x) GPU Dispatch

Evaluating a recursive binary tree in raw CUDA C++ requires manually serializing the tree into flat device buffers (cudaMalloc). You must then manage parent/child integer index offsets and launch iterative level-by-level reductions from the host:

// CUDA C++: Manual flat-array reduction kernel (cannot express recursive algebraic trees directly)
__global__ void reduce_level_kernel(const uint32_t* __restrict__ in_nodes,
                                    uint32_t* __restrict__ out_nodes,
                                    size_t num_pairs) {
    size_t idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx < num_pairs) {
        out_nodes[idx] = in_nodes[2 * idx] + in_nodes[2 * idx + 1];
    }
}

In Bend 2, you write standard algebraic recursion and append ! (sum!(lft)) to dispatch the parallel fork onto the GPU worklist shader via the 128 × 128 Task Cube (bendlang/bend):

type Tree:
  case @Leaf:
    val: U32
  case @Node:
    lft: Tree
    rgt: Tree

def sum(t: Tree) -> U32:
  match t:
    case @Leaf{val}:
      val
    case @Node{lft, rgt}:
      # '!' offloads recursive branches to Metal/CUDA GPU worklist lanes
      a b = sum!(lft) sum!(rgt)
      a + b

4. Architecture Decision Matrix: Raw CUDA / Metal vs. Bend 2

Workload Pattern Recommended Target Why
Dense Matrix Multiply (GEMM), FlashAttention, Conv2D Raw CUDA C++ (cuBLAS, Cutlass) or Metal (MPSGraph) Requires hardware Tensor Cores (mma.sync / simdgroup_matrix) and tiled shared memory (__shared__) (source).
Cellular Automata, N-Body Gravity, Mandelbrot, Merkle Trees, BVH Raytracing Bend 2 PAR-GPU (f!(x)) Achieves 30.6x to 123.9x GPU speedup on Apple M4 Max from pure functional code with zero shader boilerplate (source).
Backtracking Search (queens), Genetic ASTs (symreg), HAMT Maps, Lexers Bend 2 PAR-CPU (16 cores) Runs 8.9x to 11.9x faster than sequential CPU and beats the GPU by 2.0x to 5.4x due to zero warp divergence (source).
GPU Algorithms Requiring Mathematical Correctness Proofs Bend 2 (LAWS.bend + PROOF.bend) Verifies equational laws in 0.38 s before compiling the exact same .bend file to Metal or CUDA (source).