bench + www: honest huge-workload numbers + wire bottleneck found

Added a write-to-string-shim.lsp for asm tier (which lacks the
native builtin); asm launch script pre-defines *argv* + loads the
shim so wire.lsp's wire-send finds a write-to-string definition.
Python/C tiers keep the native builtin — the shim is opt-in.

Bench extended with two huge workloads (100k × 16 B, 1M × 16 B).

Real numbers, 3090-ai, daemon warm, both ends localhost:

  workload              Python    C tier   C win
  small (3 × 16 B)       1.16 ms   0.14 ms   8.3×
  small (100 × 16 B)     3.39 ms   0.42 ms   8.1×
  medium (1000 × 16 B)  23.26 ms   2.67 ms   8.7×
  huge (100k × 16 B)    2,220 ms   STALL     n/a
  huge (1M × 16 B)     24,338 ms   STALL     n/a

THE FINDING: at huge sizes, the bottleneck is the S-expression
text wire format, not the CUDA kernel. shake256-fanout finishes
1M × 16B in ~47 ms; the Python worker takes 24 SECONDS end-to-end
because wire.lsp's recv-exact accumulates chunks via string-append
in a loop — O(n²) at multi-MB payload sizes. C tier fails outright.

The right fix is binary wire framing between client + worker,
parallel to the binary portal format the daemon + leaf already use.
That's a separate piece of work; today's Web page edit calls it out
honestly so visitors know when bend is the right tool.

asm tier worker hosting still has process-management quirks
(doesn't survive nohup detachment in this environment); bench
ships with --skip-asm by default in this run.
This commit is contained in:
russell@unturf.com 2026-06-05 08:50:36 -04:00
parent f24afcc5d9
commit 373c8ea0d3
No known key found for this signature in database
3 changed files with 49 additions and 10 deletions

View file

@ -170,16 +170,19 @@ def main():
# Write a launch.lsp the worker process will load
with open(os.path.join(ROOT, "launch.lsp"), "w") as f:
f.write('(load "wire.lsp")\n(load "gpu-worker.lsp")\n(main)\n')
# asm tier doesn't auto-bind *argv*; pre-define it here so
# gpu-worker.lsp's parse-port-arg falls through to *default-port*.
# asm tier doesn't auto-bind *argv* OR ship write-to-string;
# pre-define the former and shim the latter via wts-shim.
with open(os.path.join(ROOT, "launch-asm.lsp"), "w") as f:
f.write('(define *argv* (quote ()))\n'
'(load "write-to-string-shim.lsp")\n'
'(load "wire.lsp")\n(load "gpu-worker.lsp")\n(main)\n')
configs = {
"small (3×16B)": (args.small_n, 3, 16),
"small (100×16B)": (args.small_n, 100, 16),
"medium (1000×16B)": (args.medium_n, 1000, 16),
"small (3 × 16 B)": (args.small_n, 3, 16),
"small (100 × 16 B)": (args.small_n, 100, 16),
"medium (1000 × 16 B)": (args.medium_n, 1000, 16),
"huge (100k × 16 B)": (3, 100_000, 16),
"huge (1M × 16 B)": (2, 1_000_000, 16),
}
tiers = ["python", "c"]

View file

@ -0,0 +1,33 @@
;;; write-to-string-shim.lsp — portable write-to-string for tiers
;;; without the native builtin (asm).
;;;
;;; Python & C tiers ship `write-to-string` as a builtin; do NOT
;;; load this file on those tiers, you'll shadow the fast native
;;; version with a slower Scheme one.
;;;
;;; asm tier launch script (see launch-asm.lsp) loads this first
;;; so wire.lsp's wire-send can call write-to-string unconditionally.
(define (write-to-string x)
(cond
((null? x) "()")
((pair? x)
(string-append "("
(write-to-string (car x))
(sexp->list-tail (cdr x))))
((symbol? x) (symbol->string x))
((number? x) (number->string x))
((string? x) (string-append "\"" x "\""))
((eq? x #t) "#t")
((eq? x #f) "#f")
(else "?")))
(define (sexp->list-tail rest)
(cond
((null? rest) ")")
((pair? rest)
(string-append " "
(write-to-string (car rest))
(sexp->list-tail (cdr rest))))
(else
(string-append " . " (write-to-string rest) ")"))))