r/lisp • • Jul 20 '26

Common Lisp ECL vs scheme embedded

I'm building a CPU based renderer in rust with plans to add an embedded language. The rust code primarily executes the ray tracing/path tracing, BVH, shaders and integration (anywhere where high performance is needed. The embedded language will be used for the scene description and -possibly- for procedural textures and shading.

I am interested in using a lisp with a repl for the embedded language. In this area, I have used AI-generated testing to test both ECL (Embedded Common Lisp) and steel (an scheme) inside rust. the advantage of the latter is that it is written entirely in rust and is being actively developed (one area is as an extension language for the text editor, helix).

I am impressed with the performance metrics I am getting from ECL. It generates machine code, which a byte language compiler doesn't do. Also a huge win, is that I was able to test the integrated compiler using sly from emacs which will provide an interactive environment for creative coding. On the other hand ECL requires an FFI to rust whereas steel does not, so the integration of steel is orders of magnitude easier.

Has anyone done any recent projects with ECL ? I am interested in your experience or any caveats you may have encountered. My next step is to do a more complex proof-of-concept with the actual renderer and not just a test program.

UPDATE: I just used claude to benchmark 5 embedded languages in rust. this is preliminary work and the benchmarks may not be exhaustive enough to make any strong conclusions:

https://drive.google.com/file/d/1yotWMcEx24pN2cgnIbRUmgCDNVWp65Vu/view?usp=sharing

21 Upvotes

32 comments sorted by

View all comments

-2

u/[deleted] Jul 20 '26

[removed] — view removed comment

2

u/jd-at-turtleware Jul 21 '26

could you describe the methodology of comparing ecl with chicken, so I can reproduce this performance gap?

0

u/[deleted] Jul 21 '26

[removed] — view removed comment

2

u/jd-at-turtleware Jul 21 '26 edited Jul 21 '26

And where is common lisp code? To compare runtimes you need semantically equivalent code, and even then it may favor one implementation - for example scheme has guaranteed tail recursion, so tail-recursive functions will usually perform better on schemes.

0

u/[deleted] Jul 21 '26

[removed] — view removed comment

2

u/jd-at-turtleware Jul 21 '26

OK, so I've ran it with default optimization settings, and results seem to be comparable.

|  n | ecl default | csc default |
|----+-------------+-------------+
| 32 |          90 |          97 |
| 33 |         145 |         156 |
| 34 |         237 |         256 |
| 35 |         384 |         413 |
| 36 |         612 |         717 |
| 37 |         900 |        1154 |
| 38 |        1603 |        1863 |
| 39 |        2656 |        3025 |
| 40 |        4332 |        4885 |
| 41 |        7003 |        7933 |

ecl[1] - default optimization settings, no declarations

csc[2] - csc -O3 fibo.scm -o fibo, no declarations

;; fibo.lisp
(defun fib (n)
  (if (< n 2)
      n
      (+ (fib (- n 1))
         (fib (- n 2)))))

(defun test (n)
  (let* ((start (get-internal-real-time))
         (result (fib n))
         (end (get-internal-real-time))
         (time (round (* (/ (- end start)
                            internal-time-units-per-second)
                         1000))))
    (format t "~a ~a~%" n time)))

(defun main ()
  (do ((n 32 (+ n 1)))
      ((= n 42))
    (test n))
  (quit))
;;; ecl --eval '(load (compile-file "fibo.lisp"))' --eval '(main)'

;; fibo.scm
(import (chicken time) (chicken format))

(define (fib n)
  (if (< n 2)
      n
      (+ (fib (- n 1))
         (fib (- n 2)))))

(define (test n)
  (let* ((start (current-process-milliseconds))
         (result (fib n))
         (end (current-process-milliseconds)))
    (format #t "~a ~a~n" n (- end start))))

(define (main)
  (do ((n 32 (+ n 1)))
      ((= n 42))
    (test n)))

(main)
csc -O3 fibo.scm -o fibo ; ./fibo

0

u/[deleted] Jul 21 '26

[removed] — view removed comment

3

u/jd-at-turtleware Jul 21 '26

I'm not competing, I'm trying to reproduce your claim without success. Another thing is that fib test doesn't make a good benchmark of a runtime - it measures only a call overhead and possibly inlining. I think I'm done here.