Выбрать главу

Another code-tuning tool built into Lisp is the function DISASSEMBLE. The exact behavior of this function is implementation dependent because it depends on how the implementation compiles code—whether to machine code, bytecodes, or some other form. But the basic idea is that it shows you the code generated by the compiler when it compiled a specific function.

Thus, you can use DISASSEMBLE to see whether your declarations are having any effect on the code generated. And if your Lisp implementation uses a native compiler and you know your platform's assembly language, you can get a pretty good sense of what's actually going on when you call one of your functions. For instance, you could use DISASSEMBLE to get a sense of the difference between the first version of add, with no declarations, and the final version. First, define and compile the original version.

(defun add (x y) (+ x y))

Then, at the REPL, call DISASSEMBLE with the name of the function. In Allegro, it shows the following assembly-language-like dump of the code generated by the compiler:

CL-USER> (disassemble 'add)

;; disassembly of #<Function ADD>

;; formals: X Y

;; code start: #x737496f4:

0: 55 pushl ebp

1: 8b ec movl ebp,esp

3: 56 pushl esi

4: 83 ec 24 subl esp,$36

7: 83 f9 02 cmpl ecx,$2

10: 74 02 jz 14

12: cd 61 int $97 ; SYS::TRAP-ARGERR

14: 80 7f cb 00 cmpb [edi-53],$0 ; SYS::C_INTERRUPT-PENDING

18: 74 02 jz 22

20: cd 64 int $100 ; SYS::TRAP-SIGNAL-HIT

22: 8b d8 movl ebx,eax

24: 0b da orl ebx,edx

26: f6 c3 03 testb bl,$3

29: 75 0e jnz 45

31: 8b d8 movl ebx,eax

33: 03 da addl ebx,edx

35: 70 08 jo 45

37: 8b c3 movl eax,ebx

39: f8 clc

40: c9 leave

41: 8b 75 fc movl esi,[ebp-4]

44: c3 ret

45: 8b 5f 8f movl ebx,[edi-113] ; EXCL::+_2OP

48: ff 57 27 call *[edi+39] ; SYS::TRAMP-TWO

51: eb f3 jmp 40

53: 90 nop

; No value

Clearly, there's a bunch of stuff going on here. If you're familiar with x86 assembly language, you can probably tell what. Now compile this version of add with all the declarations.

(defun add (x y)

(declare (optimize (speed 3) (safety 0)))

(declare (fixnum x y))

(the fixnum (+ x y)))

Now disassemble add again, and see if the declarations had any effect.

CL-USER> (disassemble 'add)

;; disassembly of #<Function ADD>

;; formals: X Y

;; code start: #x7374dc34:

0: 03 c2 addl eax,edx

2: f8 clc

3: 8b 75 fc movl esi,[ebp-4]

6: c3 ret

7: 90 nop

; No value

Looks like they did.

Delivering Applications

Another topic of practical importance, which I didn't talk about elsewhere in the book, is how to deliver software written in Lisp. The main reason I neglected this topic is because there are many different ways to do it, and which one is best for you depends on what kind of software you need to deliver to what kind of user with what Common Lisp implementation. In this section I'll give an overview of some of the different options.

If you've written code you want to share with fellow Lisp programmers, the most straightforward way to distribute it is as source code.[329] You can distribute a simple library as a single source file, which programmers can LOAD into their Lisp image, possibly after compiling it with COMPILE-FILE.

More complex libraries or applications, broken up across multiple source files, pose an additional challenge—in order to load and compile the code, the files need to be loaded and compiled in the correct order. For instance, a file containing macro definitions must be loaded before you can compile files that use those macros. And a file containing DEFPACKAGE forms must be loaded before any files that use those packages can even be READ. Lispers call this the system definition problem and typically handle it with tools called system definition facilities or system definition utilities, which are somewhat analogous to build tools such as make or ant. As with make and ant, system definition tools allow you to specify the dependencies between different files and then take care of loading and compiling the files in the correct order while trying to do only work that's necessary—recompiling only files that have changed, for example.

These days the most widely used system definition tool is ASDF, which stands for Another System Definition Facility.[330] The basic idea behind ASDF is that you define systems in ASD files, and ASDF provides a number of operations on systems such as loading them or compiling them. A system can also be defined to depend on other systems, which will be loaded as necessary. For instance, the following shows the contents of html.asd, the ASD file for the FOO library from Chapters 31 and 32:

(defpackage :com.gigamonkeys.html-system (:use :asdf :cl))

(in-package :com.gigamonkeys.html-system)

(defsystem html

:name "html"

:author "Peter Seibel <peter@gigamonkeys.com>"

:version "0.1"

:maintainer "Peter Seibel <peter@gigamonkeys.com>"

:license "BSD"

:description "HTML and CSS generation from sexps."

:long-description ""

:components

((:file "packages")

(:file "html" :depends-on ("packages"))

(:file "css" :depends-on ("packages" "html")))

:depends-on (:macro-utilities))

If you add a symbolic link to this file from a directory listed in asdf:*central-registry*,[331] then you can type this:

(asdf:operate 'asdf:load-op :html)

to compile and load the files packages.lisp, html.lisp, and html-macros.lisp in the correct order after first making sure the :macro-utilities system has been compiled and loaded. For other examples of ASD files, you can look at this book's source code—the code from each practical chapter is defined as a system with appropriate intersystem dependencies expressed in the ASD files.

вернуться

329

The FASL files produced by COMPILE-FILE are implementation dependent and may or may not be compatible between different versions of the same Common Lisp implementation. Thus, they're not a very good way to distribute Lisp code. The one time they can be handy is as a way of providing patches to be applied to an application running in a known version of a particular implementation. Applying the patch simply entails LOADing the FASL, and because a FASL can contain arbitrary code, it can be used to upgrade existing data as well as to provide new code definitions.

вернуться

330

ASDF was originally written by Daniel Barlow, one of the SBCL developers, and has been included as part of SBCL for a long time and also distributed as a stand-alone library. It has recently been adopted and included in other implementations such as OpenMCL and Allegro.

вернуться

331

On Windows, where there are no symbolic links, it works a little bit differently but roughly the same.