Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions bench/RESULTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# shen-go Benchmark Results

Machine: Linux amd64
Kernel: S39.2
Date: 2026-05-05

## Measurements

### `tak(18,12,6)` single call (wall time, seconds)

| Milestone | Time (s) | vs baseline |
|---|---|---|
| Baseline (tree-walker, interpreter only) | 0.088 | 1× |
| Phase 0 (float-fix only, no perf change) | 0.088 | 1× |
| Phase 2 (bytecode VM, indexed slots) | 0.013 | **6.6×** |
| Phase 3+5 (arithmetic fast paths + self-tail loop) | 0.006 | **14.7×** |

### `(sum 0 5000000)` tail-recursive integer loop (wall time in Go tests)

| Milestone | Time (s) |
|---|---|
| Baseline | 2.99 |
| Phase 2 VM | 0.24 |
| Phase 3+5 (self-tail + fast integer =,-) | 0.05 |

Speedup on tight tail-call loop: **60×**

### `fib(30)` (non-tail double recursion)

| Milestone | Time (s) |
|---|---|
| Phase 3+5 | 0.29 |

---

## Method

```
# Define tak via KL defun, then time with get-time run:
printf '(defun tak (X Y Z) ...) (get-time run) (tak 18 12 6) (get-time run)\n' \
| ./shen-go/shen
# tak time = t2 - t1
```

---

## Notes

- Phase 0: fixed float comparison bug (`mustInteger` → `mustNumber` for `<`, `<=`, `>`, `>=`).
No performance change.
- Phase 2: `defun` now compiles to a bytecode VM with flat indexed slots (no alist env).
Both REPL-defined KL `defun` and Shen-level `define` are compiled.
`lambda`/`freeze`/`trap-error`/`let`/`cond` all compile to bytecode.
Closures capture upvalues by value at creation time.

---

## Remaining gap to shen-cl

shen-cl (SBCL) typically runs `tak(18,12,6)` in under 0.002s.
Current gap: ~6.5× (0.013s vs ~0.002s).
Target: within 3–5× of shen-cl.

Next steps to close the gap:
- Phase 4: decision-tree pattern matching compilation
- Inline allocation pooling to reduce GC pressure
57 changes: 57 additions & 0 deletions bench/bench.shen
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
\ Benchmark suite for shen-go compiler work.
\ Measures tak (arithmetic/recursion), fib (recursion), nrev (list), map-double (HOF).
\ Run: printf '(load "bench/bench.shen")\n' | ./shen

(define bench-tak
X Y Z N ->
(if (= N 0)
done
(let _ (tak X Y Z)
(bench-tak X Y Z (- N 1)))))

(define tak
X Y Z ->
(if (not (< Y X))
Z
(tak (tak (- X 1) Y Z)
(tak (- Y 1) Z X)
(tak (- Z 1) X Y))))

(define fib
0 -> 0
1 -> 1
N -> (+ (fib (- N 1)) (fib (- N 2))))

(define iota-help
N Acc -> Acc where (= N 0)
N Acc -> (iota-help (- N 1) (cons N Acc)))

(define iota
N -> (iota-help N []))

(define nrev
[] -> []
[H | T] -> (append (nrev T) [H]))

(define map-double
[] -> []
[H | T] -> (cons (* H 2) (map-double T)))

(define run-bench
Name Thunk ->
(let T0 (get-time run)
_ (Thunk)
T1 (get-time run)
(do (output "~A: ~A s~%" Name (- T1 T0))
(- T1 T0))))

(output "~%--- shen-go benchmarks ---~%")

(run-bench "tak(18,12,6)x200" (freeze (bench-tak 18 12 6 200)))
(run-bench "fib(25)" (freeze (fib 25)))
(let L (iota 1000)
(run-bench "nrev(1000)" (freeze (nrev L))))
(let L (iota 10000)
(run-bench "map-double(10000)" (freeze (map-double L))))

(output "~%--- done ---~%")
Loading
Loading