lumbda

bend — dispatch to a GPU without rewriting your code

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:

workloadPy S-expPy binaryC S-expC binary
100 × 16 B3.43 ms0.74 ms0.40 ms0.15 ms
1k × 16 B23.24 ms0.76 ms2.77 ms0.22 ms
10k × 16 B218.82 ms1.27 msCLIFF0.88 ms
100k × 16 B2,219 ms10.18 msCLIFF10.35 ms
1M × 16 B23,811 ms159 msCLIFF157 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_batchesshotswire-scpu-msgpu-msgpu/cpu
1645.5425,1070.008
161,0247.38505,8000.146
644,09611.43,4446,2160.553
1288,19217.17,0606,6041.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

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

formstatushardwarespeedup vs CPUwire 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-weighted600–1000× vs numpy+MKLTitan X vs 12-core Xeon E5-1650HIGH — undefect corpus dedup, CVE shard clusteringsrc-d/minhashcuda
cuda-cuckoo-filter378× insert, 258× deleteA100 (TCF on Perlmutter)HIGH — foxhop ECDSA candidate-pruning, undefect URL-seen filterarXiv:2603.15486
cuda-aes-ctr-chacha20211–400 GB/s (ChaCha8 / ChaCha20)single GPU (RTX 3070 sustaining 672 Gbps Poly1305)HIGH — AEAD on lumbda portal envelopes between tiersAsyncGBP
cuda-suffix-array-skew30–242× vs CPU SA-ISTesla K20MEDIUM — substring search for undefect source-corpus scansLiu/Luo
cuda-kdtree-build30–242× build, 1.6–200× kNNRTX (RT cores)MEDIUM — spatial index for unsandbox fleet localityZhou et al.
cuda-sat-paraFROST-elim93× peak, 48× avg on variable elimNVIDIA + CADICAL/Kissat baselineHIGH — ECDSA / reversible-circuit equivalence checking via CNFParaFROST
cuda-aho-corasick-pfac~50–100× (IDS pkt-inspect)GTX-classHIGH — secret/CVE-string scan across OSS source mirrorsPFAC
cuda-dilithium-pqsig57.7× keygen+sign+verify vs single CPU threadRTX 3090 TiHIGH — PQ migration for unsandbox TLS, foxhop disclosure signingIACR 2024/1365
cuda-mc-options-pricing25–152× (barrier-call kernel 152×)Tesla C1060 / modernLOW — calibration form, well-understood arithmeticGPU Gems Ch.45
cuda-cuFFT-batched-1D8–32× vs MKL; tcFFT 1.1–3.2× vs cuFFTV100 / A100MEDIUM — spectrogram dispatch for punters-cc audio correlationtcFFT
cuda-blake3-tree~5–20× (tree mode)Blaze-3 CUDAHIGH — content-addressed lumbda portal frames, foxhop attachmentsBlaze-3
cuda-bloom-filter-modern~6× CPU; 3.4 B inserts/sB200 / PerlmutterHIGH — foxhop candidate-pruning, undefect scan-target known-setarXiv:2512.15595
cuda-gemm-batched-FP84.8× FP8 vs A100; 716 TFLOPS H100H100 SXMLOW — calibration form, lattice-PQC matrix substratecuBLAS 12.0
cuda-batched-matrix-inverse4.3–16.8× vs MAGMAP100 (650–800 GF SP)LOW — linear-algebra verifiers on reversible-circuit checkingSuperfri 2018
cuda-hash-join-radix4 B tuples/s single; 1.8 T tuples/s on 1024 A100A100 clusterMEDIUM — undefect CVE↔commit↔package joinsADMS-21
cuda-kmer-count4–6× vs KMC2; ~2× Jellyfish/KMC1RapidGKC, GerbilLOW — bioinformatics adjacency; identical bend-portal shapeRapidGKC
cuda-cgraph-traversal38 B TEPS; PageRank half-billion nodes in secondsDGX2MEDIUM — undefect dependency-DAG analytics, upstream call-graphscuGraph
cuda-triangle-count-TRUST~1 T TEPS (first trillion-TEPS triangle counter)multi-A100MEDIUM — community-structure detection for twitter-x-puntersTRUST
cuda-ldpc-bp-decoder10 Gbps with early-terminationGPGPULOW — PQ-KEM noise modelling, SDR experiments on radio nodesMDPI Electronics 2022
cuda-nvcomp-zstd2.2× decompress (zstd); 1.4× LZ4; 1.9× snappyH100 / A100HIGH — undefect corpus shards, lumbda portal envelopes, permacomputer ingestnvCOMP

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-lbm100–200× vs ANSYS Fluent / OpenFOAM; 8,799 MLUPS single A100A100unsandbox MEDIUM (HPC reproducibility, OpenCL backend matches our fleet)FluidX3D
cuda-mfcc-spectral~97× CPU MFCC; STFT ~75× via cuSignal vs SciPyGTX 580 / RTX 30-seriesunsandbox HIGH — punters-cc, BT-DISC forensics, real-time CC pipelinecuSignal
cuda-batched-lp-simplex95× over CPLEX; 5× over GLPK on a batch of 100K LPsGTX 980-classunsandbox HIGH — resource scheduling, Prime Mission workstation balancingarXiv 1802.08557
cuda-betweenness-centrality-weighted30–150× warp-centric weighted BCGTX onwardsundefect HIGH — workaholic-node detection on dependency DAG, directly matches MOAD-0001 modelarXiv 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-gasal2CUDASW++4.0 16.2× over v3.0; 134× over ADEPT; 5.71 TCUPS on H100; GASAL2 packing 750× vs NVBioH100 (TCUPS)undefect MEDIUM (binary-diff & patch-similarity at scale: SW reduces to opcode-sequence diff)CUDASW++4.0
cuda-loopy-bp-mrf45× over CPU LBP for stereo MRF inferenceGTX 280-class+undefect HIGH — LBP substrate for FuzzingBrain-style probabilistic program analysisarXiv 2509.22337
cuda-sgm-stereo42 fps at 640×480 with 128 disparities on Tegra X1; 46 fps on discrete GPUsTegra X1 / discreteunsandbox MEDIUM (embedded ARM+CUDA matches our edge node profile)arXiv 1610.04121
cuda-hungarian-lap10–50× class; 400 M-variable LAP in ~13 sNVIDIA GPUunsandbox HIGH — workstation-to-queue balancing per Prime Mission; defect-cluster ↔ patch-bundle assignment for undefectScienceDirect
cuda-msm-bls12-38127.86× over Pippenger (RELIC) AVX baseline; 60% of Groth16 prover time on single GPUA100 / RTX 4090ECDSA HIGH — Pippenger bucket sort + multi-G1 arithmetic shares branchless modmul shape with our reversible secp256k1 inner loopSimdMSM TCHES
cuda-pdwt-lifting15.9× over best optimized CPU DWT (lifting scheme)GTX / Teslaunsandbox MEDIUM (audio-IPC payload analysis, BT signal denoising)PDWT
cuda-tensornet-contract8–20× vs CuPy on contraction; tensor QR ~100× vs Xeon 8480+; tensor SVD ~10×A100ECDSA HIGH — alternative to stabilizer/kickmix sim path; MPS/PEPS evaluates reversible secp256k1 circuits beyond CliffordcuTensorNet
cuda-ega-gpu-aggregation6.45–29.12× over CPU multi-pass EGA; group-by hash 19.4×NVIDIA GPUundefect HIGH — defect-corpus aggregation at planetary scale; unsandbox HIGH — telemetry queue aggregationVLDB Top-k EGA
cuda-bicgstab-ilu-spmvSpTRSV 10.7×; ILU0 BiCGSTAB 3.2× vs cuSPARSE on MI210; GMRES(30) block-ISAI 1.4–6.9×V100 / MI210ECDSA MEDIUM (sparse LA over GF(p) underpins lattice / index-calc); undefect MEDIUM (spectral analysis on DAG)arXiv 2508.04917
cuda-icicle-snark-groth16ICICLE-Snark fastest Groth16 today; Mina GPU 3× over libsnark; NTT 91% of prover at large sizesRTX 4090 / A100ECDSA 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

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.