bend: defensive errors when daemons missing + demo round-trips real ops
gpu-worker.lsp — handle-cuda-shake-fanout, handle-cuda-sim-ops-bin,
and the four binary handlers (BSHK/BCGB/BSCP/BSRT/BSB3) now check
for the registered daemon (or, for sim-ops-bin, the bend-cuda
binary) before dispatching. Missing daemon returns (error
(daemon-not-registered <op>)) over S-exp wire, or "BERRdaemon-not-
registered: <op>" over binary wire. Before this commit any caller
whose worker host lacked a CUDA binary saw the child process crash
on (cdr #f) and got HTTP 502 / empty response with no useful
diagnostic.
Factored two helpers: daemon-or-error (S-exp result) and
with-required-daemon (binary-mode wrapper). Both keep the original
handler bodies untouched on the happy path; the guard adds one
assoc lookup per request.
www/playground/demos/bend-gpu.lsp + wasm/app/demos/bend-gpu.lsp —
demo was sending (cuda-shake256-fanout COUNT 32), an op the
dispatcher doesn't know AND a signature handle-cuda-shake-fanout
doesn't accept (it takes (inputs out-bytes)). Replaced with a
three-step probe: (ping) → (health) → small (cuda-shake-fanout
("00" "01" "deadbeef") 32). Each step prints its result so the user
gets feedback at every stage of the round-trip. Note added in the
header that browser-side bend!-call is asm-tier-only today; pyodide
and emcc tier wiring is the next commit.
This commit is contained in:
parent
ee5e997df7
commit
755a4f42ef
3 changed files with 140 additions and 82 deletions
|
|
@ -196,23 +196,52 @@
|
|||
(car (cdr x)))
|
||||
(else x)))
|
||||
|
||||
;; daemon-or-error op-name → daemon-pair on success, or (error
|
||||
;; (daemon-not-registered <op>)) S-exp on failure. Used by every
|
||||
;; handle-cuda-* so a worker host that lacks a particular binary
|
||||
;; returns a clean structured error instead of crashing the child
|
||||
;; on (cdr #f). Callers branch on (pair? result) → real daemon vs
|
||||
;; (eq? (car result) 'error) → propagate the error S-exp upstream.
|
||||
(define (daemon-or-error op-name)
|
||||
(let ((d (assoc op-name *daemons*)))
|
||||
(cond
|
||||
((eq? d #f) (list 'error (list 'daemon-not-registered op-name)))
|
||||
(else (cdr d)))))
|
||||
|
||||
;; with-required-daemon — guards a binary-wire handler against missing
|
||||
;; daemons. If the named daemon is registered, calls (body daemon-pair).
|
||||
;; If not, sends "BERRdaemon-not-registered: <op>" over the binary wire
|
||||
;; so the caller gets a clean error frame instead of an empty/closed
|
||||
;; connection from a crashed child.
|
||||
(define (with-required-daemon client op-name body)
|
||||
(let ((d (assoc op-name *daemons*)))
|
||||
(cond
|
||||
((eq? d #f)
|
||||
(wire-send-raw client
|
||||
(string-append "BERRdaemon-not-registered: "
|
||||
(symbol->string op-name))))
|
||||
(else (body (cdr d))))))
|
||||
|
||||
(define (handle-cuda-shake-fanout args)
|
||||
(let* ((inputs (unquote-list (car args)))
|
||||
(out-bytes (car (cdr args)))
|
||||
(in-path (gensym-path "/tmp/bend-in" ".portal"))
|
||||
(out-path (gensym-path "/tmp/bend-out" ".portal"))
|
||||
(daemon (cdr (assoc 'cuda-shake-fanout *daemons*))))
|
||||
(write-shake-input-portal! in-path inputs out-bytes)
|
||||
(let ((status (daemon-process daemon in-path out-path #f)))
|
||||
(cond
|
||||
((eq? status 'ok)
|
||||
(let ((result (read-shake-output-portal out-path)))
|
||||
(delete-file in-path)
|
||||
(delete-file out-path)
|
||||
(list 'ok result)))
|
||||
(else
|
||||
(delete-file in-path) (delete-file out-path)
|
||||
(list 'error (cdr status)))))))
|
||||
(let ((d (daemon-or-error 'cuda-shake-fanout)))
|
||||
(cond
|
||||
((and (pair? d) (eq? (car d) 'error)) d)
|
||||
(else
|
||||
(let* ((inputs (unquote-list (car args)))
|
||||
(out-bytes (car (cdr args)))
|
||||
(in-path (gensym-path "/tmp/bend-in" ".portal"))
|
||||
(out-path (gensym-path "/tmp/bend-out" ".portal")))
|
||||
(write-shake-input-portal! in-path inputs out-bytes)
|
||||
(let ((status (daemon-process d in-path out-path #f)))
|
||||
(cond
|
||||
((eq? status 'ok)
|
||||
(let ((result (read-shake-output-portal out-path)))
|
||||
(delete-file in-path)
|
||||
(delete-file out-path)
|
||||
(list 'ok result)))
|
||||
(else
|
||||
(delete-file in-path) (delete-file out-path)
|
||||
(list 'error (cdr status))))))))))
|
||||
|
||||
;;; -- op handler -- cuda-sim-ops-bin -----------------------------
|
||||
;;;
|
||||
|
|
@ -269,6 +298,8 @@
|
|||
(portal-path (gensym-path "/tmp/bend-sim-ops" ".portal"))
|
||||
(t-start (current-time-ms)))
|
||||
(cond
|
||||
((not (file-exists? *binary-bend-cuda*))
|
||||
(list 'error (list 'bend-cuda-binary-missing *binary-bend-cuda*)))
|
||||
((not (file-exists? ops-path))
|
||||
(display ";;; bend ERROR cuda-sim-ops-bin ops-bin-missing ")
|
||||
(display ops-path) (newline)
|
||||
|
|
@ -507,29 +538,34 @@
|
|||
;; binary result, prepends "BSHR" magic, wire-send-raws it back.
|
||||
|
||||
(define (handle-binary-shake client payload)
|
||||
(let* ((daemon (cdr (assoc 'cuda-shake-fanout *daemons*)))
|
||||
(in-path (gensym-path "/tmp/bend-bin-in" ".bin"))
|
||||
(out-path (gensym-path "/tmp/bend-bin-out" ".bin"))
|
||||
(portal-blob (substring payload 4 (string-length payload))))
|
||||
(write-binary-file in-path portal-blob)
|
||||
(let ((status (daemon-process daemon in-path out-path #t)))
|
||||
(cond
|
||||
((eq? status 'ok)
|
||||
(let ((result-blob (read-binary-file out-path)))
|
||||
(if (file-exists? in-path) (delete-file in-path))
|
||||
(if (file-exists? out-path) (delete-file out-path))
|
||||
(wire-send-raw client (string-append "BSHR" result-blob))))
|
||||
(else
|
||||
(if (file-exists? in-path) (delete-file in-path))
|
||||
(if (file-exists? out-path) (delete-file out-path))
|
||||
(wire-send-raw client (string-append "BERR" (cdr status))))))))
|
||||
(let ((d (daemon-or-error 'cuda-shake-fanout)))
|
||||
(cond
|
||||
((and (pair? d) (eq? (car d) 'error))
|
||||
(wire-send-raw client "BERRdaemon-not-registered: cuda-shake-fanout"))
|
||||
(else
|
||||
(let* ((daemon d)
|
||||
(in-path (gensym-path "/tmp/bend-bin-in" ".bin"))
|
||||
(out-path (gensym-path "/tmp/bend-bin-out" ".bin"))
|
||||
(portal-blob (substring payload 4 (string-length payload))))
|
||||
(write-binary-file in-path portal-blob)
|
||||
(let ((status (daemon-process daemon in-path out-path #t)))
|
||||
(cond
|
||||
((eq? status 'ok)
|
||||
(let ((result-blob (read-binary-file out-path)))
|
||||
(if (file-exists? in-path) (delete-file in-path))
|
||||
(if (file-exists? out-path) (delete-file out-path))
|
||||
(wire-send-raw client (string-append "BSHR" result-blob))))
|
||||
(else
|
||||
(if (file-exists? in-path) (delete-file in-path))
|
||||
(if (file-exists? out-path) (delete-file out-path))
|
||||
(wire-send-raw client (string-append "BERR" (cdr status)))))))))))
|
||||
|
||||
;; Binary wire for bend form B (cuda-bignum-cgbn).
|
||||
;; Payload begins with "BCGB"; pass entire blob through to the daemon,
|
||||
;; which expects the same magic + header it received from the client.
|
||||
(define (handle-binary-cgbn client payload)
|
||||
(let* ((daemon (cdr (assoc 'cuda-bignum-cgbn *daemons*)))
|
||||
(in-path (gensym-path "/tmp/bend-cgbn-in" ".bin"))
|
||||
(with-required-daemon client 'cuda-bignum-cgbn (lambda (daemon)
|
||||
(let* ((in-path (gensym-path "/tmp/bend-cgbn-in" ".bin"))
|
||||
(out-path (gensym-path "/tmp/bend-cgbn-out" ".bin"))
|
||||
(t-start (current-time-ms)))
|
||||
(write-binary-file in-path payload)
|
||||
|
|
@ -553,15 +589,15 @@
|
|||
(display wall-ms) (newline)
|
||||
(if (file-exists? in-path) (delete-file in-path))
|
||||
(if (file-exists? out-path) (delete-file out-path))
|
||||
(wire-send-raw client (string-append "BERR" (cdr status)))))))))
|
||||
(wire-send-raw client (string-append "BERR" (cdr status)))))))))))
|
||||
|
||||
;; Binary wire for bend form A (cuda-secp256k1-batched-mul).
|
||||
;; Payload begins with "BSCP"; pass entire blob through to the daemon,
|
||||
;; which expects the same magic + header. Response payload begins
|
||||
;; with "BSCR" on success or "BERR" on failure.
|
||||
(define (handle-binary-secp client payload)
|
||||
(let* ((daemon (cdr (assoc 'cuda-secp256k1-batched-mul *daemons*)))
|
||||
(in-path (gensym-path "/tmp/bend-secp-in" ".bin"))
|
||||
(with-required-daemon client 'cuda-secp256k1-batched-mul (lambda (daemon)
|
||||
(let* ((in-path (gensym-path "/tmp/bend-secp-in" ".bin"))
|
||||
(out-path (gensym-path "/tmp/bend-secp-out" ".bin"))
|
||||
(t-start (current-time-ms)))
|
||||
(write-binary-file in-path payload)
|
||||
|
|
@ -585,7 +621,7 @@
|
|||
(display wall-ms) (newline)
|
||||
(if (file-exists? in-path) (delete-file in-path))
|
||||
(if (file-exists? out-path) (delete-file out-path))
|
||||
(wire-send-raw client (string-append "BERR" (cdr status)))))))))
|
||||
(wire-send-raw client (string-append "BERR" (cdr status)))))))))))
|
||||
|
||||
;; Binary wire for Wave-2 form `cuda-blake3-tree` (BLAKE3 batched fan-out).
|
||||
;; Payload begins with "BSB3". The .cu daemon (blake3-fanout.cu) expects
|
||||
|
|
@ -594,8 +630,8 @@
|
|||
;; entire payload through verbatim. The daemon's response file already
|
||||
;; begins with "BSR3"; pass through unchanged.
|
||||
(define (handle-binary-blake3 client payload)
|
||||
(let* ((daemon (cdr (assoc 'cuda-blake3-tree *daemons*)))
|
||||
(in-path (gensym-path "/tmp/bend-blake3-in" ".bin"))
|
||||
(with-required-daemon client 'cuda-blake3-tree (lambda (daemon)
|
||||
(let* ((in-path (gensym-path "/tmp/bend-blake3-in" ".bin"))
|
||||
(out-path (gensym-path "/tmp/bend-blake3-out" ".bin"))
|
||||
(t-start (current-time-ms)))
|
||||
(write-binary-file in-path payload)
|
||||
|
|
@ -619,15 +655,15 @@
|
|||
(display wall-ms) (newline)
|
||||
(if (file-exists? in-path) (delete-file in-path))
|
||||
(if (file-exists? out-path) (delete-file out-path))
|
||||
(wire-send-raw client (string-append "BERR" (cdr status)))))))))
|
||||
(wire-send-raw client (string-append "BERR" (cdr status)))))))))))
|
||||
|
||||
;; Binary wire for bend form G (cuda-radix-sort).
|
||||
;; Payload begins with "BSRT"; pass entire blob through to the daemon,
|
||||
;; which expects the same magic + header. Response begins with "BSRR"
|
||||
;; on success or "BERR" on failure.
|
||||
(define (handle-binary-sort client payload)
|
||||
(let* ((daemon (cdr (assoc 'cuda-radix-sort *daemons*)))
|
||||
(in-path (gensym-path "/tmp/bend-sort-in" ".bin"))
|
||||
(with-required-daemon client 'cuda-radix-sort (lambda (daemon)
|
||||
(let* ((in-path (gensym-path "/tmp/bend-sort-in" ".bin"))
|
||||
(out-path (gensym-path "/tmp/bend-sort-out" ".bin"))
|
||||
(t-start (current-time-ms)))
|
||||
(write-binary-file in-path payload)
|
||||
|
|
@ -651,7 +687,7 @@
|
|||
(display wall-ms) (newline)
|
||||
(if (file-exists? in-path) (delete-file in-path))
|
||||
(if (file-exists? out-path) (delete-file out-path))
|
||||
(wire-send-raw client (string-append "BERR" (cdr status)))))))))
|
||||
(wire-send-raw client (string-append "BERR" (cdr status)))))))))))
|
||||
|
||||
;; Accept one client, handle one request, close. Returns #t to keep
|
||||
;; serving, #f when the server should stop.
|
||||
|
|
|
|||
|
|
@ -1,28 +1,39 @@
|
|||
; bend dispatch — ship a workload to a CUDA worker.
|
||||
;
|
||||
; This is a CPU-impossible problem at the chosen scale: SHAKE256 fan-out
|
||||
; over 1M inputs takes about 10 seconds on a 3090 via lumbda's binary
|
||||
; bend protocol (see whitepaper §11), and about 2 minutes on a host CPU.
|
||||
; This demo refuses to run locally — set a bend URL in the ⚡ bar
|
||||
; above first. The playground guards the Run button so a customer
|
||||
; machine never burns minutes on the local tiers.
|
||||
;
|
||||
; This demo refuses to run locally — set a bend URL in the bar above
|
||||
; first. The playground guards the Run button so a customer machine
|
||||
; never burns minutes on the local tiers.
|
||||
; What you can call today (S-expression mode over HTTP):
|
||||
; (ping) → (ok pong) — round-trip probe
|
||||
; (health) → (ok (load-avg ...)) — worker telemetry
|
||||
; (cuda-shake-fanout INPUTS OUT-BYTES)
|
||||
; → (ok (HEX0 HEX1 ...)) — N SHAKE256 outputs
|
||||
; Binary modes (BSHK / BCGB / BSCP / BSRT / BSB3) and the
|
||||
; 1M-input "feel the GPU win" run stay native-only until Phase 2
|
||||
; lands server-side factory recipes — those compile per-call
|
||||
; binaries on the worker filesystem and stream them through
|
||||
; the existing handlers.
|
||||
|
||||
; Payload is the lumbda S-expression form the gpu-worker expects.
|
||||
; Replace COUNT with a smaller value (e.g. 1024) for warm-up runs and
|
||||
; a larger one (1000000+) when you want to feel the GPU win.
|
||||
(define COUNT 1000000)
|
||||
(display ";; 1. probe round-trip ...") (newline)
|
||||
(define r1 (bend!-call "(ping)"))
|
||||
(display " (ping) → ") (display r1) (newline)
|
||||
(newline)
|
||||
|
||||
(display ";; 2. worker telemetry ...") (newline)
|
||||
(define r2 (bend!-call "(health)"))
|
||||
(display " (health) → ") (display r2) (newline)
|
||||
(newline)
|
||||
|
||||
(display ";; 3. small CUDA fan-out (3 inputs, 32-byte outputs) ...") (newline)
|
||||
(define payload
|
||||
(string-append
|
||||
"(cuda-shake256-fanout "
|
||||
(number->string COUNT)
|
||||
" 32)"))
|
||||
"(cuda-shake-fanout "
|
||||
"(\"00\" \"01\" \"deadbeef\") "
|
||||
"32)"))
|
||||
(display " payload: ") (display payload) (newline)
|
||||
(define r3 (bend!-call payload))
|
||||
(display " result : ") (display r3) (newline)
|
||||
(newline)
|
||||
|
||||
(display "bend payload: ") (display payload) (newline)
|
||||
(display "sending to gpu-worker...") (newline)
|
||||
|
||||
(define result (bend!-call payload))
|
||||
|
||||
(display "result: ") (display result) (newline)
|
||||
(print "done")
|
||||
|
|
|
|||
|
|
@ -1,28 +1,39 @@
|
|||
; bend dispatch — ship a workload to a CUDA worker.
|
||||
;
|
||||
; This is a CPU-impossible problem at the chosen scale: SHAKE256 fan-out
|
||||
; over 1M inputs takes about 10 seconds on a 3090 via lumbda's binary
|
||||
; bend protocol (see whitepaper §11), and about 2 minutes on a host CPU.
|
||||
; This demo refuses to run locally — set a bend URL in the ⚡ bar
|
||||
; above first. The playground guards the Run button so a customer
|
||||
; machine never burns minutes on the local tiers.
|
||||
;
|
||||
; This demo refuses to run locally — set a bend URL in the bar above
|
||||
; first. The playground guards the Run button so a customer machine
|
||||
; never burns minutes on the local tiers.
|
||||
; What you can call today (S-expression mode over HTTP):
|
||||
; (ping) → (ok pong) — round-trip probe
|
||||
; (health) → (ok (load-avg ...)) — worker telemetry
|
||||
; (cuda-shake-fanout INPUTS OUT-BYTES)
|
||||
; → (ok (HEX0 HEX1 ...)) — N SHAKE256 outputs
|
||||
; Binary modes (BSHK / BCGB / BSCP / BSRT / BSB3) and the
|
||||
; 1M-input "feel the GPU win" run stay native-only until Phase 2
|
||||
; lands server-side factory recipes — those compile per-call
|
||||
; binaries on the worker filesystem and stream them through
|
||||
; the existing handlers.
|
||||
|
||||
; Payload is the lumbda S-expression form the gpu-worker expects.
|
||||
; Replace COUNT with a smaller value (e.g. 1024) for warm-up runs and
|
||||
; a larger one (1000000+) when you want to feel the GPU win.
|
||||
(define COUNT 1000000)
|
||||
(display ";; 1. probe round-trip ...") (newline)
|
||||
(define r1 (bend!-call "(ping)"))
|
||||
(display " (ping) → ") (display r1) (newline)
|
||||
(newline)
|
||||
|
||||
(display ";; 2. worker telemetry ...") (newline)
|
||||
(define r2 (bend!-call "(health)"))
|
||||
(display " (health) → ") (display r2) (newline)
|
||||
(newline)
|
||||
|
||||
(display ";; 3. small CUDA fan-out (3 inputs, 32-byte outputs) ...") (newline)
|
||||
(define payload
|
||||
(string-append
|
||||
"(cuda-shake256-fanout "
|
||||
(number->string COUNT)
|
||||
" 32)"))
|
||||
"(cuda-shake-fanout "
|
||||
"(\"00\" \"01\" \"deadbeef\") "
|
||||
"32)"))
|
||||
(display " payload: ") (display payload) (newline)
|
||||
(define r3 (bend!-call payload))
|
||||
(display " result : ") (display r3) (newline)
|
||||
(newline)
|
||||
|
||||
(display "bend payload: ") (display payload) (newline)
|
||||
(display "sending to gpu-worker...") (newline)
|
||||
|
||||
(define result (bend!-call payload))
|
||||
|
||||
(display "result: ") (display result) (newline)
|
||||
(print "done")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue