# bend — Lumbda's GPU dispatch primitive
bend is Lumbda's GPU dispatch primitive: cost-routed local-vs-remote execution over a wire protocol, with a catalog of CUDA forms each backed by published speedup benchmarks.

`bend` is a Lumbda primitive that decides per call whether to evaluate locally or ship to a CUDA worker over our wire protocol. Tiny inputs stay local; heavy inputs bend to a worker that holds a warm CUDA context across requests. The decision uses a cost estimator on the argument shape, not the operation name.

This page documents the protocol, the wire numbers we measured, and a catalog of forms — standalone CUDA binaries with published speedups that bend can dispatch to.

## Start a GPU worker

    # On any host with nvcc + a CUDA-capable GPU:
    make gpu-worker
    # → builds examples/cuda-fanout/shake256-fanout
    # → builds the C tier (~10× faster wire orchestration than Python)
    # → launches gpu-worker.lsp on port 9091

    # Override tier or port:
    make gpu-worker LUMBDA=python PORT=9001   # easier debugging
    make gpu-worker LUMBDA=asm                # smallest footprint

## Call it from any tier

    ;; bend works on every tier — Python, C, asm — through the same
    ;; tcp-* + portal primitives lumbda already ships.
    (load "examples/cuda-fanout/wire.lsp")
    (load "examples/cuda-fanout/bend.lsp")
    (load "examples/cuda-fanout/bend-macros.lsp")  ; Python/C only — asm uses bend-call

    ;; Tiny — cost below threshold, evaluates locally
    (bend (cuda-shake-fanout '("00" "01" "deadbeef") 32))

    ;; Heavy — cost above threshold, ships to the GPU worker
    (bend (cuda-shake-fanout one-million-inputs 32))

On a single RTX 3090 with a warm daemon, SHAKE256 fan-out matched `hashlib.shake_256` byte-for-byte and won by 1.5–10× across the workloads we measured. Below the break-even (~100 MB of bulk hash work) host CPU stays faster — the cost estimator picks correctly.

## Wire protocol — two modes

The CUDA kernel runs inside the leaf binary; what the tier choice affects is wire orchestration. The wire has two modes: S-expression text (the default — hex strings inside a Scheme list) and binary (magic `BSHK` header + raw bytes, identical layout to the daemon's binary portal). Binary mode bypasses S-expression parsing entirely:

workload | Py S-exp | Py binary | C S-exp | C binary
--- | --- | --- | --- | ---
100 × 16 B | 3.43 ms | 0.74 ms | 0.40 ms | 0.15 ms
1k × 16 B | 23.24 ms | 0.76 ms | 2.77 ms | 0.22 ms
10k × 16 B | 218.82 ms | 1.27 ms | CLIFF | 0.88 ms
100k × 16 B | 2,219 ms | 10.18 ms | CLIFF | 10.35 ms
1M × 16 B | 23,811 ms | 159 ms | CLIFF | 157 ms

Binary mode wins by 30–200× over S-expression mode at scale. At 1 M × 16 B inputs, C tier binary is 157 ms end-to-end versus 23,811 ms for the S-exp path — a 150× speedup. The CUDA kernel itself on this 3090 runs in ~47 ms; binary wire adds ~110 ms of file I/O + framing on top, a 2.5× multiplier instead of the 500× multiplier the S-exp path imposed.

Critically, at huge workloads bend now beats host hashlib: host SHAKE256 over 1 M tiny inputs is ~2 s on a single Python core; bend via binary worker is 157 ms — a 12× speedup of host. The cost estimator in `bend.lsp` should be updated to know about the binary path so the routing decision picks GPU at this scale instead of staying local.

Binary mode lives behind the `BSHK` magic byte in the wire payload. S-expression callers see no change; binary callers prepend the magic and send raw bytes. See `examples/cuda-fanout/bench_tiers.py --binary` for the protocol implementation.

Three tiers, three operating points (S-expression mode): C tier wins at small & medium scales (8× faster than Python); asm tier hits 0.21 ms at very small inputs (~30% behind C, 6× faster than Python, 70 KB statically linked, zero libc); Python tier scales linearly (~22 µs per input) all the way through 1 M inputs but runs slowly on a single core. The S-exp CLIFFs at 10k (C) and 1k (asm) are tier-internal reader limits — binary mode bypasses them entirely.

The CUDA toolchain stays isolated to the leaf binary the worker spawns. No tier links libcudart; no tier requires nvcc at build time. Asm tier hosts workers through hand-written `pipe2 + fork + execve` syscalls — no libc anywhere on the chain.

## Real workload — ecdsafail search

Beyond hash fan-out, bend now dispatches quantum-reversible circuit scoring for our secp256k1 point-addition challenge work at foxhop.net/ecdsa. Lumbda emits an upstream-format `ops.bin` from a Phase B Roetteler 12-step circuit, calls `(bend!-call '(cuda-sim-ops-bin path 141))`, & receives Σ Clifford / Σ Toffoli totals back from a GPU worker over our binary wire — same cross-tier validation, same byte-identical portal contract the hash demo proves. Search loops on any host tier ship candidate scoring to whichever fleet node holds a warm CUDA context.

Measured on a 3090 against HEAD's 12.8 M-op kickmix `ops.bin` (716 MB):

n_batches | shots | wire-s | cpu-ms | gpu-ms | gpu/cpu
--- | --- | --- | --- | --- | ---
1 | 64 | 5.5 | 42 | 5,107 | 0.008
16 | 1,024 | 7.3 | 850 | 5,800 | 0.146
64 | 4,096 | 11.4 | 3,444 | 6,216 | 0.553
128 | 8,192 | 17.1 | 7,060 | 6,604 | 1.07

Crossover at ~115 batches. GPU kernel carries ~5,070 ms of fixed overhead (init + alloc + upload) plus ~12 ms per batch; CPU runs ~55 ms per batch. The kickmix circuit's conditional ops cause branch divergence — this is one form where GPU does not dominate. Honest numbers go in the catalog below.

## Form catalog

A form earns a slot here only after we have published a benchmark or measured one on our hardware. "I think this would be fast" does not earn a slot — the form-status column says `planned` until numbers exist.

### Status legend

- `live` — binary built, worker dispatches it, numbers recorded on our hardware
- `surveyed` — published benchmark cited, prototype binary not yet wrapped; speedup claims need verification on our fleet before promotion to `live`
- `planned` — design slot reserved, no binary

The catalog grows continuously. Each form below carries enough metadata for anyone to start a port: canonical reference, reported speedup, target hardware, sketch wire shape. Forms with HIGH relevance to our active missions (foxhop ECDSA work, undefect defect-scanning, unsandbox / unturf permacomputer infrastructure) move up the build queue.

### Live forms

form | status | hardware | speedup vs CPU | wire shape
--- | --- | --- | --- | ---
`cuda-shake-fanout` | live | RTX 3090 | 12× over host hashlib at 1 M × 16 B inputs | `(cuda-shake-fanout '(hex ...) out-bytes)` + BSHK binary
`cuda-sim-ops-bin` | live | RTX 3090 | 1.07× at 128 batches (8192 shots); crossover ~115 batches | `(cuda-sim-ops-bin "path/to/ops.bin" n-batches)`

### Surveyed forms (published precedent, build queued)

#### A. `cuda-secp256k1-batched-mul` — batched secp256k1 scalar / point ops

Speedup: gECC 4.94× on unknown-point mul, 5.56× on ECDSA verify vs CPU. VanitySearch forks hit 6.5 Gkeys/s on RTX 4090, 8.6 Gkeys/s on RTX 5090, 2.65 Gkeys/s on RTX 3080. Endomorphism + Montgomery batch-inversion (one mod-inv per N points instead of N) carries the kernel.

Wire: `(secp-mul-batch (scalars . blob<N×32B>) (base-point . blob<64B>))` → `(points . blob<N×64B>)`

References: gECC paper · VanitySearch · VanitySearch-Bitcrack fork

#### B. `cuda-bignum-cgbn` — 256-bit modular arithmetic primitives

Speedup: 100×+ on dense mul vs Xeon-20c + GMP + OpenMP on V100 (midsize-int study).

Wire: `(cgbn-batch (op . mod-mul|mod-inv|mod-add) (modulus . blob<32B>) (a . blob<N×32B>) (b . blob<N×32B>))` → `blob<N×32B>`

References: NVlabs CGBN · midsize-int benchmarks

#### C. `cuda-rho-pollard-walk` — Pollard rho / kangaroo walks

Speedup: 87.7 M ops/sec on RTX 2070 Super for ECCp79 (Certicom challenge solved in ~3 hours). Original CUDA Pollard paper reports > 7.2 M points/sec at 256 threads on older HW.

Wire: `(rho-walk-batch (start-points . blob<W×64B>) (steps . N) (distinguished-mask . blob<32B>))` → `(distinguished . blob<K×96B>)`

References: atlomak/CUDA-rho-pollard · oritwoen/kangaroo

#### D. `cuda-clifford-stabilizer` — tableau stabilizer simulator (Stim-on-GPU)

Speedup: 186× over Stim (CPU SOTA) on equivalence-checking. STABSim a first GPU stabilizer sim to scale better than CPU on QEC workloads.

Wire: `(stab-sim-batch (n-qubits . k) (circuit . blob) (n-shots . S))` → `(samples . blob<S×ceil(k/8)>)`

References: STABSim · Qimax · equivalence-checking

#### E. `cuda-bernstein-yang-inv` — batched modular inverse (safegcd)

Speedup: 3–10× per inversion over Fermat on CPU. No published dedicated CUDA implementation found — gECC uses Montgomery's batched-inversion trick instead. Standalone Bernstein-Yang-on-CUDA holds novel territory.

Wire: `(modinv-batch (modulus . blob<32B>) (xs . blob<N×32B>))` → `blob<N×32B>`

References: safegcd · Jumping for Bernstein-Yang

#### F. `cuda-ntt-poly` — Number Theoretic Transform

Speedup: Up to 123× over CPU; 21× on RTX 3070; cuFFT-comparable kernel structure.

Wire: `(ntt (mod . p) (omega . root) (xs . blob<N×8B>))` → `blob<N×8B>`

References: NTTSuite · FHE NTT

#### G. `cuda-radix-sort` / `cuda-prefix-scan` — reduction primitives

Speedup: 1.4 G keys/sec on Titan; 20–50× over CPU merge sort; 257× over Intel Xeon Phi for scan.

Wire: `(sort-u64 (xs . blob<N×8B>))` → `blob<N×8B>`

References: NVIDIA CUB · Onesweep

### Wave 2 — broader surveyed forms (2026-06-05)

Sorted by reported speedup vs CPU descending. Speedups quoted from published benchmarks on the cited hardware; numbers labelled `surveyed` have not yet run on our fleet, so this table calls a paper a paper and a measurement a measurement.

form | speedup | hardware | relevance | ref
--- | --- | --- | --- | ---
`cuda-minhash-weighted` | 600–1000× vs numpy+MKL | Titan X vs 12-core Xeon E5-1650 | HIGH — undefect corpus dedup, CVE shard clustering | src-d/minhashcuda
`cuda-cuckoo-filter` | 378× insert, 258× delete | A100 (TCF on Perlmutter) | HIGH — foxhop ECDSA candidate-pruning, undefect URL-seen filter | arXiv:2603.15486
`cuda-aes-ctr-chacha20` | 211–400 GB/s (ChaCha8 / ChaCha20) | single GPU (RTX 3070 sustaining 672 Gbps Poly1305) | HIGH — AEAD on lumbda portal envelopes between tiers | AsyncGBP
`cuda-suffix-array-skew` | 30–242× vs CPU SA-IS | Tesla K20 | MEDIUM — substring search for undefect source-corpus scans | Liu/Luo
`cuda-kdtree-build` | 30–242× build, 1.6–200× kNN | RTX (RT cores) | MEDIUM — spatial index for unsandbox fleet locality | Zhou et al.
`cuda-sat-paraFROST-elim` | 93× peak, 48× avg on variable elim | NVIDIA + CADICAL/Kissat baseline | HIGH — ECDSA / reversible-circuit equivalence checking via CNF | ParaFROST
`cuda-aho-corasick-pfac` | ~50–100× (IDS pkt-inspect) | GTX-class | HIGH — secret/CVE-string scan across OSS source mirrors | PFAC
`cuda-dilithium-pqsig` | 57.7× keygen+sign+verify vs single CPU thread | RTX 3090 Ti | HIGH — PQ migration for unsandbox TLS, foxhop disclosure signing | IACR 2024/1365
`cuda-mc-options-pricing` | 25–152× (barrier-call kernel 152×) | Tesla C1060 / modern | LOW — calibration form, well-understood arithmetic | GPU Gems Ch.45
`cuda-cuFFT-batched-1D` | 8–32× vs MKL; tcFFT 1.1–3.2× vs cuFFT | V100 / A100 | MEDIUM — spectrogram dispatch for punters-cc audio correlation | tcFFT
`cuda-blake3-tree` | ~5–20× (tree mode) | Blaze-3 CUDA | HIGH — content-addressed lumbda portal frames, foxhop attachments | Blaze-3
`cuda-bloom-filter-modern` | ~6× CPU; 3.4 B inserts/s | B200 / Perlmutter | HIGH — foxhop candidate-pruning, undefect scan-target known-set | arXiv:2512.15595
`cuda-gemm-batched-FP8` | 4.8× FP8 vs A100; 716 TFLOPS H100 | H100 SXM | LOW — calibration form, lattice-PQC matrix substrate | cuBLAS 12.0
`cuda-batched-matrix-inverse` | 4.3–16.8× vs MAGMA | P100 (650–800 GF SP) | LOW — linear-algebra verifiers on reversible-circuit checking | Superfri 2018
`cuda-hash-join-radix` | 4 B tuples/s single; 1.8 T tuples/s on 1024 A100 | A100 cluster | MEDIUM — undefect CVE↔commit↔package joins | ADMS-21
`cuda-kmer-count` | 4–6× vs KMC2; ~2× Jellyfish/KMC1 | RapidGKC, Gerbil | LOW — bioinformatics adjacency; identical bend-portal shape | RapidGKC
`cuda-cgraph-traversal` | 38 B TEPS; PageRank half-billion nodes in seconds | DGX2 | MEDIUM — undefect dependency-DAG analytics, upstream call-graphs | cuGraph
`cuda-triangle-count-TRUST` | ~1 T TEPS (first trillion-TEPS triangle counter) | multi-A100 | MEDIUM — community-structure detection for twitter-x-punters | TRUST
`cuda-ldpc-bp-decoder` | 10 Gbps with early-termination | GPGPU | LOW — PQ-KEM noise modelling, SDR experiments on radio nodes | MDPI Electronics 2022
`cuda-nvcomp-zstd` | 2.2× decompress (zstd); 1.4× LZ4; 1.9× snappy | H100 / A100 | HIGH — undefect corpus shards, lumbda portal envelopes, permacomputer ingest | nvCOMP

### Wave 3 — surveyed 2026-06-05

15 additional forms spanning ZK / SNARK provers, pairing crypto, tensor network contraction, sparse linear algebra, CV primitives, numerical solvers, generic belief propagation, MD/CFD kernels, convex optimization, DSP beyond cuFFT, DB aggregations, graph theory beyond triangle/PageRank. Sorted by reported speedup or absolute throughput descending.

form | speedup / throughput | hardware | relevance | ref
--- | --- | --- | --- | ---
`cuda-fluidx3d-lbm` | 100–200× vs ANSYS Fluent / OpenFOAM; 8,799 MLUPS single A100 | A100 | unsandbox MEDIUM (HPC reproducibility, OpenCL backend matches our fleet) | FluidX3D
`cuda-mfcc-spectral` | ~97× CPU MFCC; STFT ~75× via cuSignal vs SciPy | GTX 580 / RTX 30-series | unsandbox HIGH — punters-cc, BT-DISC forensics, real-time CC pipeline | cuSignal
`cuda-batched-lp-simplex` | 95× over CPLEX; 5× over GLPK on a batch of 100K LPs | GTX 980-class | unsandbox HIGH — resource scheduling, Prime Mission workstation balancing | arXiv 1802.08557
`cuda-betweenness-centrality-weighted` | 30–150× warp-centric weighted BC | GTX onwards | undefect HIGH — workaholic-node detection on dependency DAG, directly matches MOAD-0001 model | arXiv 1701.05975
`cuda-cudasift-orb-ransac` | ~60× SIFT CPU→GPU (11 fps 1920×1440); 1.2 ms on GTX 1060; ORB 11.3× | GTX 1060+ | unsandbox MEDIUM (visual evidence pipeline for incident reports) | CudaSift
`cuda-cudasw-gasal2` | CUDASW++4.0 16.2× over v3.0; 134× over ADEPT; 5.71 TCUPS on H100; GASAL2 packing 750× vs NVBio | H100 (TCUPS) | undefect MEDIUM (binary-diff & patch-similarity at scale: SW reduces to opcode-sequence diff) | CUDASW++4.0
`cuda-loopy-bp-mrf` | 45× over CPU LBP for stereo MRF inference | GTX 280-class+ | undefect HIGH — LBP substrate for FuzzingBrain-style probabilistic program analysis | arXiv 2509.22337
`cuda-sgm-stereo` | 42 fps at 640×480 with 128 disparities on Tegra X1; 46 fps on discrete GPUs | Tegra X1 / discrete | unsandbox MEDIUM (embedded ARM+CUDA matches our edge node profile) | arXiv 1610.04121
`cuda-hungarian-lap` | 10–50× class; 400 M-variable LAP in ~13 s | NVIDIA GPU | unsandbox HIGH — workstation-to-queue balancing per Prime Mission; defect-cluster ↔ patch-bundle assignment for undefect | ScienceDirect
`cuda-msm-bls12-381` | 27.86× over Pippenger (RELIC) AVX baseline; 60% of Groth16 prover time on single GPU | A100 / RTX 4090 | ECDSA HIGH — Pippenger bucket sort + multi-G1 arithmetic shares branchless modmul shape with our reversible secp256k1 inner loop | SimdMSM TCHES
`cuda-pdwt-lifting` | 15.9× over best optimized CPU DWT (lifting scheme) | GTX / Tesla | unsandbox MEDIUM (audio-IPC payload analysis, BT signal denoising) | PDWT
`cuda-tensornet-contract` | 8–20× vs CuPy on contraction; tensor QR ~100× vs Xeon 8480+; tensor SVD ~10× | A100 | ECDSA HIGH — alternative to stabilizer/kickmix sim path; MPS/PEPS evaluates reversible secp256k1 circuits beyond Clifford | cuTensorNet
`cuda-ega-gpu-aggregation` | 6.45–29.12× over CPU multi-pass EGA; group-by hash 19.4× | NVIDIA GPU | undefect HIGH — defect-corpus aggregation at planetary scale; unsandbox HIGH — telemetry queue aggregation | VLDB Top-k EGA
`cuda-bicgstab-ilu-spmv` | SpTRSV 10.7×; ILU0 BiCGSTAB 3.2× vs cuSPARSE on MI210; GMRES(30) block-ISAI 1.4–6.9× | V100 / MI210 | ECDSA MEDIUM (sparse LA over GF(p) underpins lattice / index-calc); undefect MEDIUM (spectral analysis on DAG) | arXiv 2508.04917
`cuda-icicle-snark-groth16` | ICICLE-Snark fastest Groth16 today; Mina GPU 3× over libsnark; NTT 91% of prover at large sizes | RTX 4090 / A100 | ECDSA MEDIUM (zk + MSM stack shares finite-field discipline); undefect MEDIUM (zk-prover defect scanning) | ICICLE-Snark

Wave 3 filter-outs: AMGX algebraic multigrid (2–5×), GROMACS GPU (2–3×), NVOFA optical flow (7–10× borderline, dedicated hardware unit), Junction-tree BP per-message (0.68–9.18×), batched L-BFGS (134× single-case, not generalized). All below 10× or insufficiently general; revisit when shape changes.

### Skipped — revisit when shape changes

- Argon2 / scrypt: ~1000 H/s on Tesla K20X is the whole point of memory-hard KDFs. Not a speedup story; only worth listing in an attack-surface doc.
- cuRAND alone: already bundled inside Monte Carlo + ChaCha20 forms; not a wire-protocol form by itself.
- GP regression / variational inference: current GPU wins are 2–4×, below Pareto-frontier threshold for public catalog.
- Generic ML inference: out of scope for this catalog — covered better by upstream frameworks.
- cuda-batched-mcts: 25–40× on Go-style rollouts; wrong shape for our current candidate search.
- cuda-faiss-ann: 5–12× over CPU FAISS; no embedding workload today.

### Why a form earns its slot

A form is GPU-worth-it when at least one of:

1. Embarrassingly parallel. N independent items, no cross-item dependency. SHAKE fan-out, batched mod-mul, bulk point-add — each thread owns one item.
2. Dense, branch-free inner loop. Same operation on every element. Matrix-vector, convolution, bit-twiddling sweeps.
3. Reduction-friendly. Tree-reduce / prefix-sum / parallel-scan patterns GPU hardware accelerates natively.
4. Big batch amortizes fixed kernel overhead. Our `cuda-sim-ops-bin` shows ~5 s kernel overhead; only worth it past ~115 batches.

When none of these hold, do not force the problem onto GPU. Find a different decomposition: parallelize on a different axis (per-candidate instead of per-shot), or stay on CPU & fan out across fleet hosts.

## Recommended build order

1. A — `cuda-secp256k1-batched-mul`: biggest immediate win. VanitySearch's CUDA secp256k1 kernel hits 6.5 Gkeys/s on a 4090; bend gets a GPU-rate point-mul oracle for candidate validation.
2. D — `cuda-clifford-stabilizer`: the right axis fix for our 1.07× `cuda-sim-ops-bin` ceiling. Reshape circuit-sim per-candidate-parallel; targets a Clifford fragment via STABSim-style tableau.
3. B — `cuda-bignum-cgbn`: foundational layer. Generic 256-bit vocabulary lumbda calls without committing to a curve.

Forms E, C, G follow once A–D give us measured numbers on our hardware. F & below revisit when our shape changes.

## Source & specs

examples/cuda-fanout/ — wire contract, daemon protocol, bench data, per-tier integration sketch.
CATALOG.md — canonical source for form metadata; this page renders from the same data.