Re: Timing Comparison?
"David McClain (as dbm at refined-audiometrics dot com)" <[email protected]>
| Newsgroups | gmane.lisp.lispworks.general |
|---|---|
| Message-ID | <[email protected]> |
Well, I found most of the problem. SBCL compiles EVERYTHING before ever trying to execute any of it. LW only compiles if you ask it to do so.
The nature of Forth is that words are created on the fly with arbitrary behavior codes. So I set up a method on the construction of every Forth word to ensure that its behavior code is a compiled function.
(defgeneric ensure-compiled-function (fn)
(:method ((fn symbol))
(ensure-compiled-function (symbol-function fn)))
(:method ((fn function))
#+:LISPWORKS
(if (sys:compiled-code-p fn)
fn
(compile nil fn))
#-:LISPWORKS
fn))
(defmethod initialize-instance :after ((obj <code-def>) &key &allow-other-keys)
(setf (fw-cfa obj) (ensure-compiled-function (fw-cfa obj))))
And I put in a trap in the inner interpreter to catch any words being executed without also having a compiled behavior function (essentially, a Lisp Thunk).
And so now, LWM is only about 3x slower than SBCL.
> On Oct 31, 2025, at 18:33, David McClain (as dbm at refined-audiometrics dot com) <[email protected]> wrote:
>
> I just happened to notice that things on SBCL 2.5.10 on my M1 iMac were running considerably faster than the same code running on LWM 8.1.1.
>
> For arcane reasons*, I have a Forth system written in Lisp, in which I have the Forth interpreter running the output of a Scheme compiler whose intermediate language is Forth. So Scheme compiled to intermediate Forth, Forth machine in Lisp.
>
> [*Arcane reasons: Back in 2011 we were waiting for USPTO Review of an audio processing system. So to allow people to evaluate the algorithm in live settings, we needed to implement an obfuscated version of the audio processing algorithm.
>
> We obfuscated by constructing a Forth Metacompiler in LW, and target compiled a Forth implementation of the audio processing algorithm, using a token threaded Forth interpreter in the embedded C code of the test application. The tokens were randomly assigned, and different for every production of the headless Forth dictionary image.
>
> This produces a totally gibberish appearing blob of memory in the middle of the C application, but which can be executed by an extremely tiny collection of Forth primitives coded in C alongside the dictionary blob.
>
> We were eventually awarded the US Patents on our processing methods.]
>
> I compiled the following Scheme code for a “fast” version of Fibonacci numbers:
>
> (define (fast-fib n)
> (if (< n 2)
> 1
> (let f ((ix 2)
> (f1 1)
> (f2 1))
> (if (< ix n)
> (f (+ ix 1) f2 (+ f1 f2))
> f2))
> ))
>
> This compiles down to the following intermediate code:
>
> new-vars | fast-fib |
>
> #{
> 1 chkargs
> 0 @shallow
> 2
> <
> if
> 1
> else
> NIL
> 1 +env
> #{
> 3 chkargs
> 0 @shallow
> 0 2 @deep
> <=
> if
> 0 @shallow
> 1
> +
> 2 @shallow
> 1 @shallow
> 2 @shallow
> +
> 0 1 @deep
> 3 jmp invoke
> else
> 2 @shallow
> then
> }#
> 0 !shallow
> drop
> 2
> 1
> 1
> 0 @shallow
> 3 jmp invoke
> then
> }#
> FAST-FIB scheme-!
> drop
>
> All of these items in the compiled output are Forth verbs in my ForthRPL system. All of the Forth verbs have native compiled behavior codes which either run Code definitions, or which interpret trees of Forth symbols in higher level Forth colon-definitions.
>
> Never mind the details. All of the Lisp code consists essentially of natively compiled Thunks in the Forth system. And this is true for both LWM and SBCL.
>
> To test the speed of execution, I perform 3 duration measurements, and report the median time. To get to meaningful timings, I run FastFib on 10_000, and measure using my Lisp interface to the system gettimeofday() routine. This interface differs between the LWM version and the SBCL version.
>
> So to test that both Lisp’s are measuring the same kind of duration, we first test the gettimeofday() interface to produce the following result:
>
> ;; Measure what we measure with USEC:GET-TIME-USEC
> (vm:median
> (coerce
> (loop repeat 3 collect
> (let ((start (usec:get-time-usec)))
> (sleep 1)
> (- (usec:get-time-usec) start)))
> 'vector))
> ;; => 1_002_853 SBCL 2.5.10 on M1 iMac
> ;; => 1_004_157 LWM 8.1.1 on M1 iMac
>
> This seems close enough between them, to better than 3 significant figures.
>
> So now here are the results of taking the median of 3 duration timings of:
>
> 10_000 fast-fib
>
> ;; 10_000 med3 => 1_412_582 (LWM 8.1.1 on M1 iMac)
> ;; 10_000 med3 => 52_201 (SBCL 2.5.10 on M1 iMac)
>
> SBCL appears to be running 27x faster on this arcane exercise. Both LWM and SBCL are running the exact same Lisp code, AFAIK, except for the USEC:GET-TIME-USEC function.
>
> How can the difference between these two Lisp implementations be that great??
>
>
>
>