Compilation speed of CL implementations
These last few days, I worked a lot on make-website, including support for both Clozure CL and ECL in addition to the de facto reference implementation that is SBCL.
One of the underlying goals was to see if I could speed it up, because while SBCL is the leading impl. concerning execution speed, its compilation speed suffers from all the optimizations. And I have good reasons to think that my bottleneck is the macro-expansion of all the spinneret tags and the associated GC stress, here.
So without further ado, here's how good (or bad) these three fare when used to (re)build this website (durations in seconds):
SBCL | CCL | ECL | SBCL's interpreter | |
---|---|---|---|---|
From scratch | 70.1 | 33.0 | 261.1 | 2.5 |
One doc changed | 0.6 | 0.4 | 2.2 | 0.0 |
No change | 0.1 | 0.2 | 0.2 | 0.0 |
Looks like putting in one hour to support CCL was worth it, but switching to SBCL's interpreter obsoleted all the others for this use case! Keep in mind that adding a new page or changing a title guarantees an almost full rebuild (because of the inlined and page-specific sidebar, can't use Iframes here), so this matters more than you may have thought.
Might ECL be hampered by its use of boehm-gc? Apparently, it's using gcc to compile everything, I must try to use its bytecode compiler but ECL seems to be currently broken on my machine… I'll update this page if I manage to fix it.
Benchmark details
- Versions used: SBCL 2.5.4, CCL 1.13, ECL 24.5.10
- CPU: AMD 5900X
- RAM: 64 GB DDR4
- OS: Gentoo Linux
For fairness, I added a global (optimize (compilation-speed 3))
declaration in make-website.lisp
(where spinneret is called) and I tried giving each a heap size of
32 GiB and kept the best result (SBCL' interpreter got almost two times slower with it).
I also tried after rebuilding ECL with --enable-{gen,precise}gc
, to no avail; it
actually got ~10% slower, even with half the "gc count"…
Raw benchmark log
[sbcl] From scratch sbcl --dynamic-space-size 32768 --script make.lisp Evaluation took: 70.088 seconds of real time 67.258411 seconds of total run time (63.604077 user, 3.654334 system) [ Real times consist of 1.676 seconds GC time, and 68.412 seconds non-GC time. ] [ Run times consist of 1.676 seconds GC time, and 65.583 seconds non-GC time. ] 95.96% CPU 8,416 forms interpreted 310,403 lambdas converted 259,332,400,267 processor cycles 35,628,858,640 bytes consed [sbcl] One file touched sbcl --dynamic-space-size 32768 --script make.lisp Evaluation took: 0.647 seconds of real time 0.632841 seconds of total run time (0.536662 user, 0.096179 system) 97.84% CPU 28 forms interpreted 2,845 lambdas converted 2,398,210,352 processor cycles 311,402,848 bytes consed [sbcl] No change sbcl --dynamic-space-size 32768 --script make.lisp Evaluation took: 0.097 seconds of real time 0.081726 seconds of total run time (0.049787 user, 0.031939 system) 84.54% CPU 383 lambdas converted 362,481,008 processor cycles 31,102,912 bytes consed [ccl] From scratch </dev/null ccl -R 34359738368 -b -Q -l make.lisp (LET ((*SRC-ROOT* #P"src/") ... (MAKE-WEBSITE)) took 32,960,000 microseconds (32.960000 seconds) to run. 1,654,613 microseconds ( 1.654613 seconds, 5.02%) of which was spent in GC. During that period, and with 24 available CPU cores, 30,746,420 microseconds (30.746420 seconds) were spent in user mode 810,946 microseconds ( 0.810946 seconds) were spent in system mode 5,115,814,944 bytes of memory allocated. 328,817 minor page faults, 0 major page faults, 0 swaps. [ccl] One file touched </dev/null ccl -R 34359738368 -b -Q -l make.lisp (LET ((*SRC-ROOT* #P"src/") ... (MAKE-WEBSITE)) took 393,344 microseconds (0.393344 seconds) to run. 40,406 microseconds (0.040406 seconds, 10.27%) of which was spent in GC. During that period, and with 24 available CPU cores, 356,686 microseconds (0.356686 seconds) were spent in user mode 29,940 microseconds (0.029940 seconds) were spent in system mode 52,122,400 bytes of memory allocated. 12,137 minor page faults, 0 major page faults, 0 swaps. [ccl] No change </dev/null ccl -R 34359738368 -b -Q -l make.lisp (LET ((*SRC-ROOT* #P"src/") ... (MAKE-WEBSITE)) took 168,695 microseconds (0.168695 seconds) to run. 35,505 microseconds (0.035505 seconds, 21.05%) of which was spent in GC. During that period, and with 24 available CPU cores, 143,708 microseconds (0.143708 seconds) were spent in user mode 20,069 microseconds (0.020069 seconds) were spent in system mode 13,007,568 bytes of memory allocated. 11,336 minor page faults, 0 major page faults, 0 swaps. [ecl] From scratch ecl --norc --quiet --heap-size 34359738368 --shell make.lisp ;;; Loading #P"/home/anon/.local/lib/quicklisp/setup.lisp" ;;; Loading #P"/usr/lib64/ecl-24.5.10/asdf.fas" real time : 261.131 secs run time : 55.901 secs gc count : 296 times consed : 6167662912 bytes [ecl] One file touched ecl --norc --quiet --heap-size 34359738368 --shell make.lisp ;;; Loading #P"/home/anon/.local/lib/quicklisp/setup.lisp" ;;; Loading #P"/usr/lib64/ecl-24.5.10/asdf.fas" real time : 2.165 secs run time : 0.532 secs gc count : 5 times consed : 99081392 bytes [ecl] No change ecl --norc --quiet --heap-size 34359738368 --shell make.lisp ;;; Loading #P"/home/anon/.local/lib/quicklisp/setup.lisp" ;;; Loading #P"/usr/lib64/ecl-24.5.10/asdf.fas" real time : 0.187 secs run time : 0.198 secs gc count : 3 times consed : 51961600 bytes [sbcl-interp] From scratch sbcl --script make.lisp Evaluation took: 2.503 seconds of real time 1.440874 seconds of total run time (0.899308 user, 0.541566 system) [ Real times consist of 0.050 seconds GC time, and 2.453 seconds non-GC time. ] [ Run times consist of 0.053 seconds GC time, and 1.388 seconds non-GC time. ] 57.57% CPU 1,876,713 forms interpreted 170 lambdas converted 9,263,076,688 processor cycles 917,200,752 bytes consed [sbcl-interp] One file touched sbcl --script make.lisp UPDATING TARGETS - 1ST PASS... UPDATING TARGETS - 2ND PASS)... Evaluation took: 0.019 seconds of real time 0.018245 seconds of total run time (0.014359 user, 0.003886 system) 94.74% CPU 15,898 forms interpreted 91 lambdas converted 67,733,976 processor cycles 12,539,152 bytes consed [sbcl-interp] No change sbcl --script make.lisp Evaluation took: 0.003 seconds of real time 0.002838 seconds of total run time (0.002838 user, 0.000000 system) 100.00% CPU 10,497,714 processor cycles 556,320 bytes consed