Historical internal name "uncommonlisp" retired in favor of the
public name "lumbda" ahead of lumbda.com going live. Scope of
this commit:
Source files renamed:
uncommonlisp.py -> lumbda.py
asm/uncommonlisp.s -> asm/lumbda.s
c/uncommonlisp.h -> c/lumbda.h
whitepaper/uncommonlisp-whitepaper -> whitepaper/lumbda-whitepaper (.rst + .pdf)
Binaries renamed (tracked ones; c/ was always gitignored):
asm/uncommonlisp, asm/uncommonlisp-gc, asm/uncommonlisp.o,
asm/uncommonlisp-gc.o -> asm/lumbda(-gc)(.o)
c/.gitignore -> ignores lumbda
Internal string updates (sed pass ordered longest-first):
asm/uncommonlisp -> asm/lumbda
c/uncommonlisp -> c/lumbda
uncommonlisp.py -> lumbda.py
UNCOMMONLISP_BIN -> LUMBDA_BIN (asm/test.sh env var)
"uncommonlisp> " -> "lumbda> " (asm REPL prompt baked into binary)
UNCOMMONLISP -> LUMBDA (macros, comments)
uncommonlisp -> lumbda (prose)
Binary portal magic updated:
"ULPORTAL" -> "LUMBDAB1" # "Lumbda Binary v1"
Old portal files are not backward-compatible — this is a deliberate
break since it's the rename moment. S-expression portals already
carry their own ";; lumbda-portal v1" header and remain cleanly
versioned.
WHITEPAPER.pdf / WHITEPAPER.rst symlinks repointed to the renamed
files. Makefile's whitepaper target targets lumbda-whitepaper.pdf.
Not changed (intentional, separate phases):
- Filesystem directory /home/fox/git/uncommonlisp itself
(fox renames locally and the gitlab repo URL in a follow-up)
- tests.py hardcoded cwd=/home/fox/git/uncommonlisp
(matches the current on-disk location; will flip when the
directory rename ships)
- Git history (immutable; old commits still say uncommonlisp,
which is correct — that's what they were)
Verified:
137 asm no-GC + 137 asm GC + 571 Python + 83 C + 189 shared
functional tests all pass under the new names.
bench-gc-http (2000 req): all 4 cells behave as expected
(cells 1/2 flat, 3 leaks, 4 bounded at 1 chunk).
Python REPL, C REPL, asm REPL all start cleanly.
139 lines
6.4 KiB
Text
139 lines
6.4 KiB
Text
;;; portal-formats.lsp — Benchmark three portal formats
|
|
;;;
|
|
;;; 1. S-expression (portable — any implementation can read)
|
|
;;; 2. JSON (Python + C)
|
|
;;; 3. Binary heap dump (assembly only, fastest)
|
|
;;;
|
|
;;; Run in Python: python3 lumbda.py --fast tests/portal-formats.lsp
|
|
;;; Run in C: ./c/lumbda tests/portal-formats.lsp
|
|
;;; Asm: asm/lumbda < tests/portal-cross-load.lsp (no `load` builtin)
|
|
;;;
|
|
;;; ── Observed results (2026-04-16, same laptop) ─────────────────
|
|
;;;
|
|
;;; | save time | load time | file size
|
|
;;; Python S-exp | ~0.55 ms | ~0.38 ms | 310 B
|
|
;;; Python JSON | ~3.07 ms | (n/a) | 25,855 B
|
|
;;; C S-exp | ~0.17 ms | ~0.03 ms | 310 B
|
|
;;; Asm binary | ~0.08 ms | ~0.08 ms | ~10 KB
|
|
;;;
|
|
;;; Insight: S-expression is ~80x smaller than JSON for the same
|
|
;;; state, and every implementation already has a parser for it —
|
|
;;; because the language IS the interchange format.
|
|
;;;
|
|
;;; Cross-impl verified: Python→C, C→Python, Python→asm, C→asm
|
|
;;; (see tests/portal-cross-save.lsp + portal-cross-load.lsp)
|
|
|
|
;;; ═══════════════════════════════════════════════════════════════
|
|
;;; Setup: create some state to serialize
|
|
;;; ═══════════════════════════════════════════════════════════════
|
|
|
|
(define my-int 42)
|
|
(define my-list (list 1 2 3 4 5 6 7 8 9 10))
|
|
(define my-string "hello world")
|
|
(define my-nested (list (list 1 2) (list 3 4) (list 5 6)))
|
|
(define my-alist (list (cons 'a 1) (cons 'b 2) (cons 'c 3)))
|
|
|
|
(define (my-fib n)
|
|
(let loop ((a 0) (b 1) (i 0))
|
|
(if (= i n) a (loop b (+ a b) (+ i 1)))))
|
|
|
|
(define my-result (my-fib 30))
|
|
|
|
;;; ═══════════════════════════════════════════════════════════════
|
|
;;; Format 1: S-expression (portable)
|
|
;;; Write values as Scheme (define ...) forms. Any impl can (load) it.
|
|
;;; ═══════════════════════════════════════════════════════════════
|
|
|
|
(define (write-sexp-portal filename)
|
|
(let ((port (open-output-file filename)))
|
|
;; Header
|
|
(display ";; lumbda portable state (S-expression format)" port)
|
|
(newline port)
|
|
(display ";; resume: (load \"" port)
|
|
(display filename port)
|
|
(display "\")" port)
|
|
(newline port)
|
|
(newline port)
|
|
;; Values
|
|
(display "(define my-int " port) (write my-int port) (display ")" port) (newline port)
|
|
(display "(define my-list '" port) (write my-list port) (display ")" port) (newline port)
|
|
(display "(define my-string " port) (write my-string port) (display ")" port) (newline port)
|
|
(display "(define my-nested '" port) (write my-nested port) (display ")" port) (newline port)
|
|
(display "(define my-alist (list" port)
|
|
(for-each (lambda (p)
|
|
(display " (cons '" port) (write (car p) port)
|
|
(display " " port) (write (cdr p) port) (display ")" port))
|
|
my-alist)
|
|
(display "))" port) (newline port)
|
|
(display "(define my-result " port) (write my-result port) (display ")" port) (newline port)
|
|
(close-port port)))
|
|
|
|
(define t0 (current-time))
|
|
(write-sexp-portal "/tmp/state.sexp")
|
|
(define t1 (current-time))
|
|
|
|
(display "S-expression save: ")
|
|
(display (* (- t1 t0) 1000))
|
|
(display "ms") (newline)
|
|
|
|
;;; ═══════════════════════════════════════════════════════════════
|
|
;;; Verify: load it back
|
|
;;; ═══════════════════════════════════════════════════════════════
|
|
|
|
;; Clear the values
|
|
(set! my-int 0)
|
|
(set! my-list '())
|
|
(set! my-string "")
|
|
(set! my-result 0)
|
|
|
|
(define t2 (current-time))
|
|
(load "/tmp/state.sexp")
|
|
(define t3 (current-time))
|
|
|
|
(display "S-expression load: ")
|
|
(display (* (- t3 t2) 1000))
|
|
(display "ms") (newline)
|
|
|
|
;; Verify
|
|
(display "Verify: my-int=") (display my-int)
|
|
(display " my-result=") (display my-result)
|
|
(display " my-list=") (display my-list)
|
|
(newline)
|
|
|
|
(if (and (= my-int 42) (= my-result 832040) (= (length my-list) 10))
|
|
(display "PASS: S-expression portal round-trip")
|
|
(display "FAIL: S-expression portal round-trip"))
|
|
(newline)
|
|
|
|
;;; ═══════════════════════════════════════════════════════════════
|
|
;;; Format 2: JSON (via portal-save builtin if available)
|
|
;;; Python binds `portal-save` directly. C binds `portal-save!` as a
|
|
;;; VM-level checkpoint (deferred save). We probe by procedure? so this
|
|
;;; stays portable and never invokes an unbound callable.
|
|
;;; ═══════════════════════════════════════════════════════════════
|
|
|
|
(define json-saver
|
|
(guard (e (#t #f))
|
|
(if (procedure? portal-save) portal-save #f)))
|
|
|
|
(if json-saver
|
|
(let ((t4 (current-time)))
|
|
(json-saver "/tmp/state.json")
|
|
(let ((t5 (current-time)))
|
|
(display "JSON save: ")
|
|
(display (* (- t5 t4) 1000))
|
|
(display "ms") (newline)))
|
|
(display "JSON save: skipped (no direct portal-save in this impl)\n"))
|
|
|
|
;;; ═══════════════════════════════════════════════════════════════
|
|
;;; Summary
|
|
;;; ═══════════════════════════════════════════════════════════════
|
|
|
|
(newline)
|
|
(display "Portal format comparison:") (newline)
|
|
(display " S-expression: portable — every impl parses it") (newline)
|
|
(display " JSON: Python + C, graph-aware, preserves sharing") (newline)
|
|
(display " Binary: asm only — raw heap dump, same-arch restore") (newline)
|
|
(newline)
|
|
(display "Files left at /tmp/state.sexp and /tmp/state.json for size comparison.") (newline)
|
|
(display "Run `wc -c /tmp/state.*` in shell to compare sizes.") (newline)
|