code • words • emotions

Daniel Janus’s blog

Who said Common Lisp programs cannot be small?

9 August 2008

So, how much disk space does your average CL image eat up? A hundred megs? Fifty? Twenty? Five, perhaps, if you’re using LispWorks with a tree-shaker? Well then, how about this?

[nathell@chamsin salza2-2.0.4]$ ./cl-gzip closures.lisp test.gz
[nathell@chamsin salza2-2.0.4]$ gunzip test
[nathell@chamsin salza2-2.0.4]$ diff closures.lisp test
[nathell@chamsin salza2-2.0.4]$ ls -l cl-gzip
-rwxr-xr-x 1 nathell nathell 386356 2008-08-09 11:08 cl-gzip

That’s right. A standalone executable of a mini-gzip, written in Common Lisp, taking up under 400K! And it only depends on glibc and GMP, which are available by default on pretty much every Linux installation. (This is on a 32-bit x86 machine, by the way).

I used the most recent version of ECL for compiling this tiny example. The key to the size was configuring ECL with --disable-shared --enable-static CFLAGS="-Os -ffunction-sections -fdata-sections" LDFLAGS="-Wl,-gc-sections". This essentially gives you a poor man’s tree shaker for free at a linker level. And ECL in itself produces comparatively tiny code.

I built this example from Salza2’s source by loading the following code snippet:

(defvar salza
  '("package" "reset" "specials"
    "types" "checksum" "adler32" "crc32" "chains"
    "bitstream" "matches" "compress" "huffman"
    "closures" "compressor" "utilities" "zlib"
    "gzip" "user"))

(defvar salza2
  (mapcar (lambda (x) (format nil "~A.lisp" x))
          salza))

(defvar salza3
  (mapcar (lambda (x) (format nil "~A.o" x))
          salza))

(defun build-cl-gzip ()
  (dolist (x salza2)
          (load x)
          (compile-file x :system-p t))
  (c:build-program
   "cl-gzip"
   :lisp-files salza3
   :epilogue-code
     '(progn
       (in-package :salza2)
       (gzip-file (second (si::command-args))
                  (third (si::command-args))))))

(build-cl-gzip)

(Sadly enough, there’s no ASDF in here. I have yet to figure out how to leverage ASDF to build small binaries in this constrained environment.)

This gave me a standalone executable 1.2 meg in size. I then proceeded to compress it with UPX (with arguments --best --crp-ms=999999) and got the final result. How cool is that?

I am actively looking for a new job. If you happen to like my writings and think I might be just the right man for the team you’re building up, please feel free to consult my résumé or pass it on.

Update 2010-Jan-17: the above paragraph is no longer valid.