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.
205 lines
6.4 KiB
C
205 lines
6.4 KiB
C
/*
|
|
* bench.c — Benchmarks: interpreted vs compiled vs JIT
|
|
*/
|
|
#include "lumbda.h"
|
|
#include "jit.h"
|
|
|
|
static double bench_time(void) {
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return ts.tv_sec + ts.tv_nsec / 1e9;
|
|
}
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
const char *setup;
|
|
const char *expr;
|
|
int iterations;
|
|
} Benchmark;
|
|
|
|
static Benchmark benchmarks[] = {
|
|
{
|
|
"fib-iter(30)",
|
|
"(define (fib-iter n)\n"
|
|
" (let loop ((a 0) (b 1) (i 0))\n"
|
|
" (if (= i n) a (loop b (+ a b) (+ i 1)))))\n",
|
|
"(fib-iter 30)",
|
|
10000
|
|
},
|
|
{
|
|
"fib-rec(25)",
|
|
"(define (fib-rec n)\n"
|
|
" (if (<= n 1) n (+ (fib-rec (- n 1)) (fib-rec (- n 2)))))\n",
|
|
"(fib-rec 25)",
|
|
10
|
|
},
|
|
{
|
|
"list-sum(1000)",
|
|
"(define (list-sum lst)\n"
|
|
" (let loop ((l lst) (acc 0))\n"
|
|
" (if (null? l) acc (loop (cdr l) (+ acc (car l))))))\n"
|
|
"(define big-list (iota 1000))\n",
|
|
"(list-sum big-list)",
|
|
1000
|
|
},
|
|
{
|
|
"map-square(1000)",
|
|
"(define (my-map f lst)\n"
|
|
" (if (null? lst) '()\n"
|
|
" (cons (f (car lst)) (my-map f (cdr lst)))))\n"
|
|
"(define big-list (iota 1000))\n",
|
|
"(my-map (lambda (x) (* x x)) big-list)",
|
|
100
|
|
},
|
|
{
|
|
"tak(18,12,6)",
|
|
"(define (tak x y z)\n"
|
|
" (if (not (< y x)) z\n"
|
|
" (tak (tak (- x 1) y z)\n"
|
|
" (tak (- y 1) z x)\n"
|
|
" (tak (- z 1) x y))))\n",
|
|
"(tak 18 12 6)",
|
|
10
|
|
},
|
|
{
|
|
"ack(3,4)",
|
|
"(define (ack m n)\n"
|
|
" (if (= m 0) (+ n 1)\n"
|
|
" (if (= n 0) (ack (- m 1) 1)\n"
|
|
" (ack (- m 1) (ack m (- n 1))))))\n",
|
|
"(ack 3 4)",
|
|
100
|
|
},
|
|
{
|
|
"ack(3,7)",
|
|
"(define (ack m n)\n"
|
|
" (if (= m 0) (+ n 1)\n"
|
|
" (if (= n 0) (ack (- m 1) 1)\n"
|
|
" (ack (- m 1) (ack m (- n 1))))))\n",
|
|
"(ack 3 7)",
|
|
10
|
|
},
|
|
{
|
|
"eml-compose(1M)",
|
|
/* EML insight (arXiv:2603.21852v2): eml(x,y) = exp(x) - ln(y) generates\n"
|
|
* ALL elementary functions from one operator. This benchmark exercises\n"
|
|
* integer-only EML-style composition: repeated add/sub (exp/ln analog\n"
|
|
* in the integer domain). Tests whether JIT compiles well the pattern\n"
|
|
* of composing minimal primitives into higher operations. */
|
|
"(define (eml-pow base exp)\n"
|
|
" (let loop ((e exp) (acc 1))\n"
|
|
" (if (= e 0) acc (loop (- e 1) (* acc base)))))\n"
|
|
"(define (eml-log-approx n base)\n"
|
|
" (let loop ((n n) (count 0))\n"
|
|
" (if (< n base) count (loop (- n base) (+ count 1)))))\n"
|
|
"(define (eml-compose x y)\n"
|
|
" (let ((p (eml-pow x y)))\n"
|
|
" (eml-log-approx p x)))\n",
|
|
"(eml-compose 3 7)",
|
|
100000
|
|
},
|
|
{
|
|
"named-let-sum(100k)",
|
|
/* Named-let compiles to a native loop with jmp — zero overhead. */
|
|
"(define (sum-to n)\n"
|
|
" (let loop ((i n) (acc 0))\n"
|
|
" (if (= i 0) acc (loop (- i 1) (+ acc i)))))\n",
|
|
"(sum-to 100000)",
|
|
100
|
|
},
|
|
{
|
|
"list-walk(1000)",
|
|
/* Tests JIT car/cdr/null?/cons pipeline */
|
|
"(define (list-reverse lst)\n"
|
|
" (let loop ((l lst) (acc '()))\n"
|
|
" (if (null? l) acc (loop (cdr l) (cons (car l) acc)))))\n"
|
|
"(define big-list (iota 1000))\n",
|
|
"(list-reverse big-list)",
|
|
1000
|
|
},
|
|
{NULL, NULL, NULL, 0}
|
|
};
|
|
|
|
static void run_benchmark(Benchmark *b, const char *mode) {
|
|
Env *g = make_global_env();
|
|
int count;
|
|
Value *prelude_exprs = read_all(PRELUDE, &count, false);
|
|
for (int i = 0; i < count; i++) leval(prelude_exprs[i], g);
|
|
ul_free(prelude_exprs);
|
|
|
|
g_auto_compile = false;
|
|
g_jit_enabled = false;
|
|
|
|
if (strcmp(mode, "compiled") == 0) {
|
|
g_auto_compile = true;
|
|
} else if (strcmp(mode, "jit") == 0) {
|
|
g_jit_enabled = true;
|
|
}
|
|
|
|
/* Setup */
|
|
Value *setup_exprs = read_all(b->setup, &count, false);
|
|
for (int i = 0; i < count; i++) leval(setup_exprs[i], g);
|
|
ul_free(setup_exprs);
|
|
|
|
/* Parse the benchmark expression once */
|
|
Value *bench_exprs = read_all(b->expr, &count, false);
|
|
|
|
/* Warmup */
|
|
for (int i = 0; i < 3; i++) {
|
|
for (int j = 0; j < count; j++) leval(bench_exprs[j], g);
|
|
}
|
|
|
|
/* Timed run */
|
|
double start = bench_time();
|
|
Value last = VAL_VOID;
|
|
for (int i = 0; i < b->iterations; i++) {
|
|
for (int j = 0; j < count; j++) last = leval(bench_exprs[j], g);
|
|
}
|
|
double elapsed = bench_time() - start;
|
|
|
|
char *result_str = show(last, false);
|
|
printf(" %-25s %-10s %8d iters %.3f s (%.1f us/iter) => %s\n",
|
|
b->name, mode,
|
|
b->iterations, elapsed,
|
|
(elapsed / b->iterations) * 1e6,
|
|
result_str);
|
|
ul_free(result_str);
|
|
|
|
ul_free(bench_exprs);
|
|
g_auto_compile = false;
|
|
g_jit_enabled = false;
|
|
}
|
|
|
|
int main(void) {
|
|
init_symbols();
|
|
|
|
/* Set up root error context for prelude loading */
|
|
ErrorContext root_ctx;
|
|
root_ctx.call_stack_depth = 0;
|
|
root_ctx.error_obj = VAL_NIL;
|
|
root_ctx.source_line = 0;
|
|
g_error_ctx = &root_ctx;
|
|
if (setjmp(root_ctx.jmp) != 0) {
|
|
fprintf(stderr, "fatal error: %s\n", root_ctx.message);
|
|
return 1;
|
|
}
|
|
|
|
printf("lumbda C benchmarks — interpreted vs compiled vs JIT\n");
|
|
printf("═════════════════════════════════════════════════════════════════\n");
|
|
|
|
const char *modes[] = { "interp", "compiled", "jit", NULL };
|
|
|
|
for (int i = 0; benchmarks[i].name; i++) {
|
|
for (int m = 0; modes[m]; m++) {
|
|
TRY(ctx) {
|
|
run_benchmark(&benchmarks[i], modes[m]);
|
|
} CATCH {
|
|
printf(" %-25s %-10s ERROR: %s\n", benchmarks[i].name, modes[m], ctx.message);
|
|
} ENDTRY;
|
|
}
|
|
printf(" ─────────────────────────────────────────────────────────────\n");
|
|
}
|
|
|
|
printf("═════════════════════════════════════════════════════════════════\n");
|
|
return 0;
|
|
}
|