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

19 Upvotes

32 comments sorted by

16

u/jd-at-turtleware Jul 20 '26

ECL is actively developed (I'm one of maintainers) and conforming Common Lisp implementation, so if you need to pull some libraries they will usually work (SBCL is far more popular, so it is more tested by developers though). Another good thing about ECL is that it allows inlining C code with designated operators, and it may be used without the C compiler using fully integrated bytevm.

The community is not large, but there are people making projects with ECL both inside the browser and on resource constrained devices. Many developers also test their libraries against ECL as the second (or third, after CCL) implementation to make sure that the code is portable.

As of whether steel works better for you -- assuming that you want only "scripting", then tighter integration with rust will be probably a win in a long run. ECL also doesn't accept contributions derived from LLM as legally shaky, so that may also play role in your decision.

2

u/Feisty_Bike_9614 Jul 20 '26

I don’t know whether we would want to generate AI derived patches to ECL itself ( if that’s what you mean ) , but certainly we will be generating Embedded lisp / scheme test scripts using Claude or potentially any other LLM as part of the dev process. That’s one of the goals of the work .

1

u/jd-at-turtleware Jul 20 '26

yes, that's what I've meant

1

u/valorzard Jul 22 '26

Is ECL still adding a WASM backend?

3

u/jd-at-turtleware Jul 22 '26

the backend is already added; I'm working on convenience features right now. Here is a tutorial how to build it and cross compile whole ASDF systems:

https://turtleware.eu/posts/Common-Lisp-and-WebAssembly.html

1

u/valorzard Jul 22 '26

This might sound a bit stupid, but does it compile straight to WASM or is it using emscripten?

4

u/jd-at-turtleware Jul 22 '26

It isn't stupid; I've actually made that a topic of my keynote talk at ELS'26[1]. Short answer - it is used emscripten. Longer answer - I'm experimenting with WASI, but there are some hurdles.

[1] https://www.youtube.com/watch?v=xNHjIqlITmE

1

u/Top-Routine4880 6d ago

Here is another common lisp (subset) that compiles straight to wasm.
https://making.github.io/rontolisp/docs/en/index.html

1

u/Feisty_Bike_9614 Jul 23 '26

ECL produced some of the best results in the benchmark ( along with Janet ) . I’ve been working on a proof-of-concept with the actual renderer. The big win is the able to do repl-based coding with sly in emacs which I have working. This has to be replicated with Janet and it will only be a subset of.

2

u/Feisty_Bike_9614 Jul 20 '26

Sorry for the confusion. I’m the OP but it seems Google gave me a different account when i logged in ..to post this .

1

u/soegaard Jul 20 '26

Have you looked at Scheme RS?

https://www.scheme.rs/

1

u/Feisty_Bike_9614 Jul 20 '26

Have not . I’ll have to check it out .

-3

u/corbasai Jul 20 '26

My experience with ECL not extra large. I simply cant read docs due to strange markup. Maybe becouse of in my tests ECL was 5 to 10x slower than Chicken Scheme. Both compiles to C. Due to this fact I even not take it ro the last nbody performance test. Repo. So use what you want or love. IMO in such case the better option is custom lisp like script with narrow set of types and operators for one selected task.

2

u/jd-at-turtleware Jul 20 '26

could you elaborate on the docs problem?

4

u/PropagandaOfTheDude Jul 20 '26

The CSS specifies a particular size based on pixels.

body {
    margin: 1em 125px 0 10%;
    line-height: 1.5em;
    padding: 0 2em 1em 2em;
    font: 13px Verdana,Arial, sans-serif
}

Accessibility guidelines recommend using relative sizes, which grant more freedom to user agents to scale the fonts up and down for display:

https://www.a11y-collective.com/blog/wcag-minimum-font-size/#h-importance-of-relative-units-over-absolute-ones

4

u/jd-at-turtleware Jul 21 '26

thanks, I've addressed this issue.

1

u/corbasai Jul 20 '26

I'm not designer but font size too small on the desktop. On the phone even worse, I just can't read ECL page in my glasses

3

u/jd-at-turtleware Jul 21 '26

I think that reporting such issues is healthier than discouraging people on social media from using a specific piece of software; in any case I've addressed the font size issue.

0

u/corbasai Jul 21 '26

I'm so sorry for my blindness.

2

u/jd-at-turtleware Jul 21 '26

I did not criticize you for your blindness, and the above message makes me believe that you are interacting in bad faith.

2

u/church-rosser Jul 20 '26

Strange metric

2

u/SpecificMachine1 guile Jul 21 '26

Accessibility is a strange metric?

1

u/church-rosser Jul 22 '26

Its clearly accessible.

1

u/SpecificMachine1 guile Jul 22 '26

I mean, accessibility guidelines say to use relative not absolute sizes so it isn't clear to me it is

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/corbasai Jul 21 '26

Please, show ECL seconds https://www.reddit.com/r/lisp/s/hnijqNiUxU and CPU spec [Chicken code in reply]

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/corbasai Jul 21 '26

Sorry? fibo is one liner. And not tco. Mine not.

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/corbasai Jul 21 '26

Hmm, https://www.reddit.com/r/lisp/s/SfY5xPHTEt it is current C6+Crunch numbers. Why you prefer to compete to the 2018 year?

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.

1

u/corbasai Jul 21 '26 edited Jul 21 '26

ok, C5, -no-trace -disable-interrupts -O2 and you are not using type hints like

(import (scheme base)
        (chicken base)
        (chicken type)
        (chicken time)
        (chicken process-context))

(: fibo (fixnum -> fixnum))
(define (fibo n)
  (if (< n 2)
    n
    (+ (fibo (- n 1)) (fibo (- n 2)))))
(let* ((args (command-line-arguments))
       (n (string->number (car args)))
       (ts (current-process-milliseconds))
       (r (fibo n))
       (te (current-process-milliseconds)))
  (print "fibo(" n") = " r " per " (* 1e-3 (- te ts)) " seconds."))

so $ csc -O3 -no-trace -disable-interrupts -strict-types -specialize -o fibo-chicken fibo-chicken.scm

$ ./fibo-chicken 40

fibo(40) = 102334155 per 2.433 seconds.

Again, its old CHICKEN5

PS. For the lovely downvoters mtv