You are on page 1of 13

Lisp Machines

c 1988, Paul Graham

In 1974, a group at the MIT AI Lab began work on what was at the time a radical idea: a Lisp Machine, a computer made especially to run Lisp well. The Cons and its successor, the Cadr, were single-user machines designed with uncompromising attention to the needs of Lisp programmers. They were Lisp from top to bottom from the windowing software, which was written in Lisp, right down to the microcode, which implemented many Lisp functions directly. The commercial Lisp Machines available today are descended from the Cons and the Cadr. Despite improvements in both hardware and software, the underlying ideas remain unchanged. It is those ideas we are interested in here. What makes Lisp Machines dierent? This article begins by discussing Lisp Machine software, and the style of programming it supports. The second part deals with hardware the special requirements of Lisp programs, and the hardware support for them that a Lisp Machine characteristically provides. Finally, we will look at the question that naturally follows: should you buy one?

Exploratory Programming

The original Lisp Machines were designed with two goals in mind: to support Lisp programming techniques, and to run Lisp programs eciently. With all the attention paid nowadays to benchmarks, it often seems as if the latter goal were the only one. But to Lisp Machine designers and users, the software is just as important as the hardware. If you had to put it in one sentence, the advantage of Lisp is that it takes some of the burden o the programmer and places it on the hardware instead. As hardware grows less expensive in comparison to programmer time, such an argument is increasingly convincing. The Lisp Machine programming environment is based on the same premise to do more, so that

the programmer does less. If such phrases sound familiar, it may be because Computer Aided Software Engineering (CASE) systems are supposed to do exactly that. Lisp programming environments were the original CASE tools. Part of the reason Lisp makes programming easier is that its an interactive language. When writing Lisp programs, one does not feel so keenly the cycle of editing, compiling, linking, and running. Individual functions can be compiled and linked as easily (and almost as quickly) as typing a carriage return. You can call them individually on test data. And if they dont work, you can debug them interactively. These features of Lisp have led to a unique style of programming called exploratory programming. Exploratory programming means writing programs exactly the way they told you not to write them in Computer Science 101. Instead of planning beforehand exactly what youre going to do, you begin a program with only a vague idea of its eventual structure. Lisps interactiveness makes it so painless to change code that it is reasonable to program by experimenting. You can start with a specic module of the program and grow it into the full system; or start with a general skeleton and gradually ll in the specic modules; or start with a simple, complete system and make it gradually more powerful. If youre uncertain about an idea, you just try it. This might not be a good way to manage a large, relatively straightforward software project, but in the hands of a few disciplined programmers it leads to good code. The software environment you get on a Lisp Machine is designed expressly for exploratory programming. It also happens to be an example of the kind of well-written code that exploratory programming yields. The editor, compiler, utilities, and system software on a Lisp Machine will be seamlessly integrated.

Editor

As an example, heres what its like to write code on a Symbolics Lisp Machine. When you are editing a le of Lisp, the editor sees it as code rather than merely as characters. Thus along with the usual text-editing functions, you get commands like Meta-Dot, which allows you to say: nd me the le containing such-and-such function, and load it into the editor for me, centering that functions denition in the window. If you edit the function, you can make the editor re-indent your code automatically. (Misleading indentation is a notorious source of errors, so

this can save a lot of time.) Then, with another single command, you tell the system: compile the function I just redened, and load it into the current Lisp environment. If you happen to have lost track of which code has been changed, another single command recompiles and loads all the changed functions. The edit-compile-link sequence has been reduced to a single step. Other editor commands do automatically many things that Lisp programmers would otherwise have to do by hand. For example, one can: Display an expression, correctly indented, with some or all of the macros expanded. Scroll the window so that the whole denition of the current function is visible. Search the current le for unbalanced parentheses. List all the uncompiled changes in the current le. Load into the editor all the warnings generated the last time the current le was compiled. List all the functions which call the current function. Or edit each of them in succession. When you switch to a Lisp window to try out the function on some test cases, you are not using an everyday window. All the objects which get displayed on the screen are regarded as representations of Lisp objects, not merely as characters. The representations are a all mouse-sensitive. This means that, as well as being able to cut and paste them, as you could with characters, you can ask the system to do things to the objects they represent. If the cursor is on the name of a function, one of your options is to bring it up in an editor window. If the cursor is on the name of a directory, one of the options is to list the les in it; and so on. (Since Lisp windows scroll on indenitely unless you clip them, one consequence of this design is that objects which are printed out on the screen never get garbage-collected. The designers do not feel that this is a problem.) If your program blows up on some test case, you can invoke the window debugger. This is a display-oriented utility which oers options like

disassembling the current function, or returning a value as if the failed expression had. When the cause of the problem is evident, you can ask to edit the function which blew up, and the system will nd the le containing it. To be fair, the Lisp Machine environment is not really tied to the hardware in any way. Similar environments are available on Unix workstations. But this style of programming environment originated with Lisp Machines, and a lot more man-years have gone into the development of the original versions.

System Software

In a true Lisp Machine, there is no sharply-dened barrier between language and operating system. This means, for one, that its easy to get at the innards of the system software. Its just Lisp code, after all. You can call or alter the system functions just as easily as you can any other Lisp function. (As a result, the designers have made it possible to customize the Symbolics system to a greater extent than you would ever want. In fact, if theres a general fault in Symbolics software, its that it does too much.) A second advantage of integrating language and operating system is that on a Lisp Machine its easy to make several processes share memory. In Unix, having two processes share memory is neither fast nor easy. On a Lisp machine, you get it by default, and at no extra cost in speed. A Lisp Machine is software as well as hardware. This fact makes a signicant dierence to the programmer, but its something we risk ignoring if we view a computer system as nothing more than a set of benchmarks.

Hardware

The most important force inuencing the design of a Lisp machine is Lisp itself. As a language, Lisp places an unconventional burden on conventional hardware. Here we will discuss four ways in which it does so: 1. Lisp variables are untyped. Lisp objects integers, strings, and so on are typed, but a variable could be set to an integer at one point and an array later on. 2. Lisp relies on garbage collection for deallocation. Instead of having processes give back memory when they are nished with it, a separate

program, called the garbage collector, seeks out and reclaims unused space. 3. Most programs make heavy use of linked lists. 4. Lisp programs do a lot of function calling. At least, well-designed ones do. These things can all be handled perfectly well on conventional hardware. But when you run Lisp on conventional machines there will always be a lot of wasted eort. In many cases, just by modifying the hardware slightly, you could cut in half the number of machine instructions required to perform some task, or cut in half the space occupied by some Lisp object. How did the Lisp Machines designers modify the hardware? The two most important changes were (1) A new representation for data, and (2) a special-purpose instruction set. In the most recent Symbolics architecture, memory words are 40 bits long. Of these, 32 bits are used for addresses and immediate data, as in a conventional 32-bit machine. The remaining 8 bits are used to contain information of special interest to the Lisp Machine hardware. We will see later how this information is put to use. The instruction set of a computer is its machine language: the primitive instructions into which the compiler translates user programs. In a Lisp Machine, the instruction set is custom-made to run Lisp fast. It oers as single instructions some operations which would require two or more conventional machine instructions.

Typing

Variables in Lisp are not like those in other languages. In most languages, variables are typed: a program declares the variable x to be an integer, and so when the compiler encounters an expression like x + x, it knows to compile it into an integer add. In Lisp the situation is dierent. A variable can be set to an integer one moment and a string the next. Thus, unlike compilers for conventional languages, Lisp compilers have no way of knowing at compile time what type of values they are dealing with. Types can only be determined at runtime. As a result, a Lisp program can spend a lot of its time type-checking. Lisp Machines take several steps to cut down this overhead. We noted above 5

On a conventional machine, to add two integers with type-checking requires ve machine-language instructions. TEST if operand 1 is an integer BRANCH somewhere if it isnt TEST if operand 2 is an integer BRANCH somewhere if it isnt ADD the two integers, trapping somewhere on overflow On a Lisp machine, the type-checking is done in parallel with the add. If it turns out that one of the operands wasnt an integer, the result of the add is thrown away, and the machine does, say, a oating-point add instead. If the add instruction is actually given two integers, it adds them in one cycle. A program doing all integer adds would thus run in only a fth as many clock cycles as the same program compiled as above for a conventional machine. If the TEST or BRANCH instructions took more than one cycle, or if trapping on overow wasnt supported, then the dierence in speed would be even greater. Standard processors are beginning to support operations like this. Suns recent SPARC chip includes an instruction which adds two integers in a single cycle, causing a trap if the arguments werent integers or the addition overowed. (This example is due to David Andre of Symbolics.) Figure 1: Integer Add on a Lisp Machine that the Symbolics memory word contains 8 extra bits. Of those 8, 6 bits are used to specify the type of the object stored in that word. So when an instruction is handed a Lisp object at runtime, it can easily check what its getting. How does it put that information to use? The microinstructions do type-checking in parallel with the calculation of results. <reference to sidebar> describes how integer addition with type-checking, which takes ve instructions on conventional machines, is accomplished in a single instruction by a Lisp Machine. A Lisp Machine can also do other kinds of checking in parallel. For example, Common Lisp requires that the system check all array references to make sure that they are within bounds. A Lisp Machine can perform these tests in parallel with the memory access. 6

The Common Lisp standard oers a way to achieve similar eciency on conventional hardware, but at a cost: the user must insert declarations throughout his code. To ensure that two integers are added in one cycle, you declare them both (and the result) as integers, and the compiler turns the Lisp add expression into a single integer add. On a Lisp Machine, declarations are never needed. The way the hardware is designed, code runs just as fast without them. This is an advantage because: Declarations make programs ugly and sometimes almost unreadable. Although benchmarks always use declarations, programmers hardly ever do, for just that reason. This means that the de facto speed of code under development on a Lisp machine is faster than benchmarks suggest. Error-checking on a Lisp Machine never has to be disabled. On a conventional machine, by giving the compiler declarations, what you do is eliminate type-checking, not speed it up. On a Lisp Machine, type-checking is always done, even in compiled code, at no extra cost. It may be that the last point is the crucial one for Lisp Machines, because Lisp programs are inherently the kind of programs for which runtime errorchecking is important. As David Moon points out, complex, ambitious application programs are typically never nished to the point where it is safe to declare them bug-free and remove runtime error-checking. We feel it is essential for such applications to be robust when delivered to end users, so that when something unanticipated by the programmer happens, the application will fail in an obvious, comprehensible, and controlled way, rather than just supplying the wrong answer. [Moon] For example, some Common Lisp compilers for conventional hardware will let you compile a function like (defun foo () (car 5)) which is meaningless because 5 is not a list. When foo is called, it causes an illegal memory reference, which crashes Lisp. Nothing like this can happen on a Lisp Machine. 7

Thus, for applications where reliability is critical, Lisp Machines may be justied even when, on purely economic grounds, they wouldnt be the best choice. However, it remains to be seen how many such applications are actually going to be written in Lisp.

Garbage Collection

One of the major ways in which Lisp diers from other languages is its approach to memory allocation. When Lisp starts up, a large block of memory, called the heap, is set aside for storage of data objects. When a new object is created at runtime when we use cons to create a list, for example memory to contain it is allocated from the heap. When the list is no longer needed, the memory it occupied can be reused. Its part of the denition of Lisp that memory management always be handled beneath the surface. Calls to cons must automatically appropriate new memory, and when objects are no longer needed, the memory they used must automatically be reclaimed. There are various strategies for detecting when an object is no longer needed. For our purposes, it doesnt matter how we do it all we need to know is that it can be done. An unneeded object in the heap is called garbage, and the reclamation of memory occupied by such objects is called garbage collection, or gc. From the programmers point of view, gc is when the bills come due. If a program generates a lot of garbage, its going to run slowly due to the overhead of gc. On a Lisp machine, garbage collection is less of a problem because (1) it is done more eciently, and (2) it is done gradually instead of all at once. For most users, the second dierence is probably the more important one. The rst garbage-collection strategies worked as follows. Lisp continues to allocate memory until it runs low. When the amount of free memory falls below a certain threshold, the system immediately stops whatever it was doing and garbage-collects the entire heap. Lisp systems for conventional hardware still use garbage collection strategies of this sort. To put it mildly, programmers would prefer that garbage collection werent done this way. It may, in principle, make no dierence whether garbage collection is done gradually or all at once, but it certainly feels as if there were a dierence. On a conventional workstation, when the garbage collector kicks in, you sit and stare at the screen for between 10 seconds and a minute. (On a PC, you go and get a cup of coee.) Add to this the fact

that gc often takes over halfway through drawing a picture or printing out a line of text, and you have a recipe for annoying users. A Lisp Machine uses another approach, called incremental garbage collection. In incremental gc, storage reclamation is interleaved with the execution of other programs. Its a pay-as-you-go strategy; as programs are using up heap space, the garbage collector is reclaiming it. The bills need never come due in the same sense that they do on conventional hardware. If a program generates a lot of garbage, the eect is a consistent overall decrease in speed. Incremental gc is especially well-suited to interactive work, because it continues to reclaim space even while the system is waiting for input. With other computer systems, this time (eons by the computers standards) is mostly wasted. We need not go into how incremental garbage collection is implemented. (For an explanation, see [Edwards].) The crucial point is that the system has to keep track of objects as they are read from memory. A Lisp machine includes special hardware to do this at no extra cost. The same thing could be done on conventional machines, but it would have to be done in software. Since this would lead to drastic performance problems (code could run 10 times slower), in practice only Lisp Machines can do incremental gc. As well as doing garbage collection in a less nerve-wracking way, Lisp Machines embody several techniques for doing it faster. Among the most successful is ephemeral gc. After studying the way programs behaved, people began to realize that more recently-created objects were more likely to be garbage. An object that had been in use for a long time was likely to stay in use. This realization led to a technique called ephemeral gc. By organizing the memory so that recently-created objects are stored close together, the system can cut down the amount of work it has to do in order to make sure that an object can be discarded. Lisp Machine designers say that this strategy has made garbage collection between a hundred and a thousand times faster.

Lists

Recall that a Symbolics word has 8 extra bits of information attached to it. We have already accounted for 6 of them, which are used to store type tags. The remaining two bits are used in a scheme to store lists eciently. Ordinarily, an element in a linked list takes up two words of memory:

one to contain the object stored there, and one which points to the next element in the list. Lisp machines use a technique called cdr-coding to store lists in half the space. A cdr-coded list is essentially a vector: successive elements in a list are stored in successive memory words. The Lisp Machine uses the two extra bits to distinguish between cdrcoded list and a normal one. If the cdr-code of an object in a list is cdr-next, then the next word of memory is not a pointer to the next element in the list, it is the next element itself. When some future generation of conventional hardware has a word length long enough to spare two bits for cdr-coding, then this technique will be universally used. At the moment, only Lisp Machines can do it.

Fast Function Calls

Right from the beginning, one of the aims of the Lisp Machine was to make function calls fast. Usually, a well-written Lisp program is built up out of a lot of small functions rather than a few big ones. When a Lisp implementation doesnt handle function calls eciently, you risk more than slowness, you risk the quality of your code: It is important for function calling to be as fast as possible. If it is not, eciency-minded programmers will distort their programming styles to avoid function calling, producing code that is hard to maintain, and will waste a lot of time doing optimization by hand that should have been done by the Lisp implementation itself. [Moon] On a Lisp Machine, Call and Return are oered as single microcode instructions. Call builds a new stack frame on the stack containing, among other things, the arguments to be passed to the called function. It checks for errors in parallel with the required memory references, and even knows about complicated Common Lisp features like &optional and &rest parameters, which it can handle itself. In some Lisp Machine implementations, the stack is contained in a hardware stack buer in order to cut down on memory references. The layout of stack frames is designed for maximum speed at the expense of space, but the stack buer is suciently large that it rarely overows. When no exceptions occur, the original Symbolics architecture did a oneargument function call and return in less than 20 clock cycles. The Symbolics 10

Ivory chip can do most function calls in under 20 cycles. And this speed is achieved without sacricing information needed by the debugger, which gets as much information as a conventional Lisp debugger would get when running interpreted code.

Lisp Chips

The most recent development in Lisp Machine hardware is the single-chip CPU. These chips, although radically dierent in implementation from the hardware which preceeded them, represent very little change in the underlying principles of a Lisp Machine. Their main advantage is economical. VLSI has made Lisp Machine technology faster and smaller. How much faster? The designers of Symbolics Ivory chip say that it will allow performance increases of 3-5 times that of current Symbolics products. [Baker] Oddly enough, the decrease in size is probably more important. It means that a Lisp Machine can now go inside computers made by other manufacturers (notably Apple, so far), rather than requiring its own box. This eliminates the need for a choice which, if it was uncomfortable for users, was becoming doubly so for Lisp Machine vendors.

10

Enter the Real World

The foregoing has explained how, in the abstract, Lisp Machines manage to run Lisp programs in fewer clock cycles. Returning to the real world, we encounter a fact which changes everything. Conventional hardware is implemented using more advanced technology, so a cycle on a conventional machine is always faster. David Moon writes: Hardware technology of conventional machines will always be a couple of years ahead of symbolic hardware, in cycle time and price/cycle, because of the driving force of their larger market. [Moon] Thus the advantage that Lisp Machines gain in design, they lose in implementation. Who comes out ahead? This is hard to estimate, because Lisp applications vary widely, and hardware technology is constantly changing. In most

11

cases (and this is just one opinion), conventional workstations seem to be the way to go not just because theyre cheaper, but because there are more of them, more people who know how to operate them, and more software that runs on them. The advent of embeddable Lisp Machines makes a slight dent in this argument, but not much of one. For a few applications, Lisp Machines may still be the best choice. They are most likely to be especially useful for the largest, most complex Lisp programs: Bigger programs depend more on intelligent paging and fast garbage collection, and here Lisp Machines have a substantial advantage over conventional hardware. The superiority of a Lisp Machine programming environment makes more of a dierence as programs grow larger and more complex. People developing such software are more likely to be comfortable with Lisp, and thus less likely to be intimidated by the strangeness of Lisp Machines. For purely economic reasons, Lisp Machines may never be the dominant hardware for Lisp programming. But, as this article has tried to suggest, the ideas they embody are interesting in their own right.

12

Acknowledgements
The author would like to thank David Andre, Mary Feely, Robert Morris, Howard Mullings, Steve Salzberg, and Ken Sinclair for their help and comments.

References
[Baker] Clark Baker et (many) al., The Symbolics Ivory Processor: A 40 Bit Tagged Architecture Lisp Microprocessor, Proceedings ICCD, 1987. [Edwards] Bruce Edwards, Greg Eand, and Neil Weste, The Symbolics I-Machine Architecture, A Symbolic Processor Architecture for VLSI Implementation, Proceedings ICCD, 1987. [Moon] David Moon, Symbolics Architecture, IEEE Computer, January, 1987.

13

You might also like