Compiling Chicken code (part II)

One of the cool features of Chicken is that you may choose to compile parts of your Scheme program to C, in order to get better performance.

Let’s write a (naive) implementation of the factorial function. Store it in fac.scm:

;; fac.scm

(define (fac n)
(if (<= n 1)
1
(* n (fac (- n 1)))))

Now, write a second file main.scm that uses our fac function. Normally this would be as simple as

(load "fac")

(print (fac 30))

But I’d like to include some simple benchmark code, to see how well fac performs, interpreted vs compiled. I’m using the following code to call (fac 30) 100,000 times, and time the results.

;; main.scm

(load "fac")

(define *max-iterations* 100000)

(define t1 (current-milliseconds))
(let loop ((i 0))
(unless (= i *max-iterations*)
(fac 30)
(loop (+ i 1))))
(define t2 (current-milliseconds))

(define seconds (/ (- t2 t1) 1000))
(printf "~a iterations in ~a seconds.~n" *max-iterations* seconds)

Interpreted, it takes a little more than two seconds on my machine:

$ csi -s main.scm
100000 iterations in 2.191 seconds.

We can do better than that. Let’s compile fac.scm and see if it makes a difference.

$ csc -s fac.scm  # produces fac.so
$ csi -s main.scm
100000 iterations in 0.326 seconds.

That’s right — we still run main.scm interpreted, but it now uses the *compiled* fac.scm, which makes execution much faster. ^_^

We could do a lot more with this, e.g. compile main.scm to an executable, etc, but I just wanted to demonstrate how easy it is to precompile libraries, with no change in code. (At least, not for simple cases like this… but that’s a different story.)

:: Comments (2)

Compiling Chicken code (part I)

(This post is just a quick introduction to using the Chicken compiler and interpreter.)

Consider this simple file:

;; hello.scm

(print "Hello, world!")

Save this as hello.scm.

Running this as a script in the interpreter is done as follows:

$ csi -s hello.scm

Note that if the -s is omitted, the script will still run, after which the interactive interpreter is started. This can be useful in some situations (e.g. to do post-execution inspection).

Alternatively, we may choose to compile the script to an executable:

$ csc hello.scm

(csc has a lot of options, but the simplest case does what most people would expect, i.e. it compiles an the executable named hello.)

Lo and behold, it works:

$ ./hello
Hello, world!

On my system (using Chicken 2.732, Mac OS X 10.4, Intel Dual Core) this produces a 14K executable, which seems quite reasonable. However, the way I understand it, this file dynamically links to the Chicken libraries. To create a standalone executable, we must use the -static command line option, which produces a much bigger file:

$ csc -static hello.scm
$ ls -l hello
-rwxr-xr-x   1 zephyrfa  zephyrfa  1370732 Dec 30 13:54 hello*

By the way, I’m not entirely sure that this really produces an executable that is 100% standalone… I’ll have to do more testing.

There is much, much more to the Chicken compiler. Expect to see more about it in future posts. (Like the next one… :-)

:: Comments off