程序代做 CA 94304

1 Hints for Computer System Design
Butler W. Lampson
Computer Science Laboratory Xerox Palo Alto Research Center Palo Alto, CA 94304
Studying the design and implementation of a number of computer has led to some general hints for system design. They are described here and illustrated by many examples, ranging from hardware such as the Alto and the Dorado to application programs such as Bravo and Star.

Copyright By PowCoder代写 加微信 powcoder

1. Introduction
Designing a computer system is very different from designing an algorithm:
The external interface (that is, the requirement) is less precisely defined, more complex, and more subject to change.
The system has much more internal structure, and hence many internal interfaces. The measure of success is much less clear.
The designer usually finds himself floundering in a sea of possibilities, unclear about how one choice will limit his freedom to make other choices, or affect the size and performance of the entire system. There probably isn’t a ‘best’ way to build the system, or even any major part of it; much more important is to avoid choosing a terrible way, and to have clear division of responsibilities among the parts.
I have designed and built a number of computer systems, some that worked and some that didn’t. I have also used and studied many other systems, both successful and unsuccessful. From this experience come some general hints for designing successful systems. I claim no originality for them; most are part of the folk wisdom of experienced designers. Nonetheless, even the expert often forgets, and after the second system [6] comes the fourth one.
Disclaimer: These are not
novel (with a few exceptions), foolproof recipes,
laws of system design or operation, precisely formulated,
consistent,
always appropriate,
approved by all the leading experts, or guaranteed to work.
This paper was originally presented at the. 9th ACM Symposium on Operating Systems Principles and appeared in
Operating Systems Review 15, 5, Oct. 1983, p 33-48. The present version is slightly revised.
Hints for Computer System Design July 1983 1

They are just hints. Some are quite general and vague; others are specific techniques which are more widely applicable than many people know. Both the hints and the illustrative examples are necessarily oversimplified. Many are controversial.
I have tried to avoid exhortations to modularity, methodologies for top-down, bottom-up, or iterative design, techniques for data abstraction, and other schemes that have already been widely disseminated. Sometimes I have pointed out pitfalls in the reckless application of popular methods for system design.
The hints are illustrated by a number of examples, mostly drawn from systems I have worked on. They range from hardware such as the Ethernet local area network and the Alto and Dorado personal computers, through operating systems such as the SDS 940 and the Alto operating system and programming systems such as Lisp and Mesa, to application programs such as the Bravo editor and the Star office system and network servers such as the Dover printer and the Grapevine mail system. I have tried to avoid the most obvious examples in favor of others which show unexpected uses for some well-known methods. There are references for nearly all the specific examples but for only a few of the ideas; many of these are part of the folklore, and it would take a lot of work to track down their multiple sources.
And these few precepts in thy memory Look thou character.
It seemed appropriate to decorate a guide to the doubtful process of system design with quotations from Hamlet. Unless otherwise indicated, they are taken from Polonius’ advice to Laertes (I iii 58-82). Some quotations are from other sources, as noted. Each one is intended to apply to the text which follows it.
Each hint is summarized by a slogan that when properly interpreted reveals the essence of the hint. Figure 1 organizes the slogans along two axes:
Why it helps in making a good system: with functionality (does it work?), speed (is it fast enough?), or fault-tolerance (does it keep working?).
Where in the system design it helps: in ensuring completeness, in choosing interfaces, or in devising implementations.
Fat lines connect repetitions of the same slogan, and thin lines connect related slogans.
The body of the paper is in three sections, according to the why headings: functionality (section 2), speed (section 3), and fault-tolerance (section 4).
2. Functionality
The most important hints, and the vaguest, have to do with obtaining the right functionality from a system, that is, with getting it to do the things you want it to do. Most of these hints depend on the notion of an interface that separates an implementation of some abstraction from the clients who use the abstraction. The interface between two programs consists of the set of assumptions that each programmer needs to make about the other program in order to demonstrate the correctness of his program (paraphrased from [5]). Defining interfaces is the most important part of system design. Usually it is also the most difficult, since the interface design must satisfy three conflicting requirements: an interface should be simple, it should be complete, and it should
Hints for Computer System Design July 1983 2

Why? Functionality Does it work?
Is it fast enough?
Shed load End-to-end Safety first
Make it fast
Split resources Static analysis Dynamic translation
Cache answers
Use brute force Compute in background Batch processing
Fault-tolerance
Does it keep working?
End-to-end
End-to-end
Log updates
Make actions atomic
Make actions atomic Use hints
Completeness
Implementation
Separate normal and worst case
Do one thing well: Don’t generalize
Get it right
Don’t hide power
Use procedure arguments Leave it to the client
Keep basic interfaces stable Keep a place to stand
Plan to throw one away Keep secrets
Use a good idea again Divide and conquer
Figure 1: Summary of the slogans
admit a sufficiently small and fast implementation. Alas, all too often the assumptions embodied in an interface turn out to be misconceptions instead. Parnas’ classic paper [38] and a more recent one on device interfaces [5] offer excellent practical advice on this subject.
The main reason interfaces are difficult to design is that each interface is a small programming language: it defines a set of objects and the operations that can be used to manipulate the objects. Concrete syntax is not an issue, but every other aspect of programming language design is present. Hoare’s hints on language design [19] can thus be read as a supplement to this paper.
2.1 Keep it simple
Perfection is reached not when there is no longer anything to add,
but when there is no longer anything to take away. (A. Saint-Exupery)
Those friends thou hast, and their adoption tried, Grapple them unto thy soul with hoops of steel; But do not dull thy palm with entertainment
Of each new-hatch’d unfledg’d comrade.
• Do one thing at a time, and do it well. An interface should capture the minimum essentials of an abstraction. Don’t generalize; generalizations are generally wrong.
Hints for Computer System Design July 1983 3

We are faced with an insurmountable opportunity. (W. Kelley)
When an interface undertakes to do too much its implementation will probably be large, slow and complicated. An interface is a contract to deliver a certain amount of service; clients of the interface depend on the contract, which is usually documented in the interface specification. They also depend on incurring a reasonable cost (in time or other scarce resources) for using the interface; the definition of ‘reasonable’ is usually not documented anywhere. If there are six levels of abstraction, and each costs 50% more than is ‘reasonable’, the service delivered at the top will miss by more than a factor of 10.
KISS: Keep It Simple, Stupid. If in doubt, leave if out. Exterminate features.
On the other hand,
(Anonymous) (Anonymous) (C. Thacker)
Everything should be made as simple as possible, but no simpler.
(A. Einstein)
Thus, service must have a fairly predictable cost, and the interface must not promise more than the implementer knows how to deliver. Especially, it should not promise features needed by only a few clients, unless the implementer knows how to provide them without penalizing others. A better implementer, or one who comes along ten years later when the problem is better understood, might be able to deliver, but unless the one you have can do so, it is wise to reduce your aspirations.
For example, PL/1 got into serious trouble by attempting to provide consistent meanings for a large number of generic operations across a wide variety of data types. Early implementations tended to handle all the cases inefficiently, but even with the optimizing compilers of 15 years later, it is hard for the programmer to tell what will be fast and what will be slow [31]. A language like Pascal or C is much easier to use, because every construct has a roughly constant cost that is independent of context or arguments, and in fact most constructs have about the same cost.
Of course, these observations apply most strongly to interfaces that clients use heavily, such as virtual memory, files, display handling, or arithmetic. It is all right to sacrifice some performance for functionality in a seldom used interface such as password checking, interpreting user commands, or printing 72 point characters. (What this really means is that though the cost must still be predictable, it can be many times the minimum achievable cost.) And such cautious rules don’t apply to research whose object is learning how to make better implementations. But since research may well fail, others mustn’t depend on its success.
Algol 60 was not only an improvement on its predecessors, but also on nearly all its successors. (C. Hoare)
Examples of offering too much are legion. The Alto operating system [29] has an ordinary read/write-n-bytes interface to files, and was extended for Interlisp-D [7] with an ordinary paging system that stores each virtual page on a dedicated disk page. Both have small implementations (about 900 lines of code for files, 500 for paging) and are fast (a page fault takes one disk access and has a constant computing cost that is a small fraction of the disk access time, and the client
Hints for Computer System Design July 1983 4

can fairly easily run the disk at full speed). The Pilot system [42] which succeeded the Alto OS follows Multics and several other systems in allowing virtual pages to be mapped to file pages, thus subsuming file input/output within the virtual memory system. The implementation is much larger (about 11,000 lines of code) and slower (it often incurs two disk accesses to handle a page fault and cannot run the disk at full speed). The extra functionality is bought at a high price.
This is not to say that a good implementation of this interface is impossible, merely that it is hard. This system was designed and coded by several highly competent and experienced people. Part of the problem is avoiding circularity: the file system would like to use the virtual memory, but virtual memory depends on files. Quite general ways are known to solve this problem [22], but they are tricky and easily lead to greater cost and complexity in the normal case.
And, in this upshot, purposes mistook
Fall’n on th’ inventors’ heads. (V ii 387)
Another example illustrates how easily generality can lead to unexpected complexity. The Tenex system [2] has the following innocent-looking combination of features:
It reports a reference to an unassigned virtual page by a trap to the user program.
A system call is viewed as a machine instruction for an extended machine, and any reference it makes to an unassigned virtual page is thus similarly reported to the user program.
Large arguments to system calls, including strings, are passed by reference.
There is a system call CONNECT to obtain access to another directory; one of its arguments is a string containing the password for the directory. If the password is wrong, the call fails after a three second delay, to prevent guessing passwords at high speed.
CONNECT is implemented by a loop of the form
for i := 0 to Length(directoryPassword) do
if directoryPassword[i] ≠ passwordArgument[i] then
Wait three seconds; return BadPassword end if
connect to directory; return Success
n The following trick finds a password of length n in 64n tries on the average, rather than 128 /2
(Tenex uses 7 bit characters in strings). Arrange the passwordArgument so that its first character is the last character of a page and the next page is unassigned, and try each possible character as the first. If CONNECT reports BadPassword, the guess was wrong; if the system reports a reference to an unassigned page, it was correct. Now arrange the passwordArgument so that its second character is the last character of the page, and proceed in the obvious way.
This obscure and amusing bug went unnoticed by the designers because the interface provided by a Tenex system call is quite complex: it includes the possibility of a reported reference to an unassigned page. Or looked at another way, the interface provided by an ordinary memory reference instruction in system code is quite complex: it includes the possibility that an improper reference will be reported to the client without any chance for the system code to get control first.
Hints for Computer System Design July 1983 5

An engineer is a man who can do for a dime
what any fool can do for a dollar. (Anonymous)
At times, however, it’s worth a lot of work to make a fast implementation of a clean and powerful interface. If the interface is used widely enough, the effort put into designing and tuning the implementation can pay off many times over. But do this only for an interface whose importance is already known from existing uses. And be sure that you know how to make it fast.
For example, the BitBlt or RasterOp interface for manipulating raster images [21, 37] was devised by after several years of experimenting with the Alto’s high-resolution interactive display. Its implementation costs about as much microcode as the entire emulator for the Alto’s standard instruction set and required a lot of skill and experience to construct. But the performance is nearly as good as the special-purpose character-to-raster operations that preceded it, and its simplicity and generality have made it much easier to build display applications.
The Dorado memory system [8] contains a cache and a separate high-bandwidth path for fast input/output. It provides a cache read or write in every 64 ns cycle, together with 500 MBits/second of I/O bandwidth, virtual addressing from both cache and I/O, and no special cases for the microprogrammer to worry about. However, the implementation takes 850 MSI chips and consumed several man-years of design time. This could only be justified by extensive prior experience (30 years!) with this interface, and the knowledge that memory access is usually the limiting factor in performance. Even so, it seems in retrospect that the high I/O bandwidth is not worth the cost; it is used mainly for displays, and a dual-ported frame buffer would almost certainly be better.
Finally, lest this advice seem too easy to take,
• Get it right. Neither abstraction nor simplicity is a substitute for getting it right. In fact, abstraction can be a source of severe difficulties, as this cautionary tale shows. Word processing and office information systems usually have provision for embedding named fields in the documents they handle. For example, a form letter might have ‘address’ and ‘salutation’ fields. Usually a document is represented as a sequence of characters, and a field is encoded by something like {name: contents}. Among other operations, there is a procedure FindNamedField that finds the field with a given name. One major commercial system for some time used a
FindNamedField procedure that ran in time O(n ), where n is the length of the document. This
remarkable result was achieved by first writing a procedure FindIthField to find the ith field (which must take time O(n) if there is no auxiliary data structure), and then implementing FindNamedField(name) with the very natural program
for i := 0 to numberofFields do
FindIthField; if its name is name then exit
Once the (unwisely chosen) abstraction FindIthField is available, only a lively awareness of its cost will avoid this disaster. Of course, this is not an argument against abstraction, but it is well to be aware of its dangers.
Hints for Computer System Design July 1983 6

2.2 Corollaries
The rule about simplicity and generalization has many interesting corollaries.
Costly thy habit as thy purse can buy,
But not express’d in fancy; rich, not gaudy.
• Make it fast, rather than general or powerful. If it’s fast, the client can program the function it wants, and another client can program some other function. It is much better to have basic operations executed quickly than more powerful ones that are slower (of course, a fast, powerful operation is best, if you know how to get it). The trouble with slow, powerful operations is that the client who doesn’t want the power pays more for the basic function. Usually it turns out that the powerful operation is not the right one.
Had I but time (as this fell sergeant, death, Is strict in his arrest) O, I could tell you — But let it be. (V ii 339)
For example, many studies (such as [23, 51, 52]) have shown that programs spend most of their time doing very simple things: loads, stores, tests for equality, adding one. Machines like the 801 [41] or the RISC [39] with instructions that do these simple operations quickly can run programs faster (for the same amount of hardware) than machines like the V AX with more general and powerful instructions that take longer in the simple cases. It is easy to lose a factor of two in the running time of a program, with the same amount of hardware in the implementation. Machines with still more grandiose ideas about what the client needs do even worse [18].
To find the places where time is being spent in a large system, it is necessary to have measurement tools that will pinpoint the time-consuming code. Few systems are well enough understood to be properly tuned without such tools; it is normal for 80% of the time to be spent in 20% of the code, but a priori analysis or intuition usually can’t find the 20% with any certainty. The performance tuning of Interlisp-D sped it up by a factor of 10 using one set of effective tools [7].
• Don’t hide power. This slogan is closely related to the last one. When a low level of abstraction allows something to be done quickly, higher levels should not bury this power inside something more general. The purpose of abstractions is to conceal undesirable properties; desirable ones should not be hidden. Sometimes, of course, an abstraction is multiplexing a resource, and this necessarily has some cost. But it should be possible to deliver all or nearly all of it to a single client with only slight loss of performance.
For example, the Alto disk hardware [53] can transfer a full cylinder at disk speed. The basic file system [29] can transfer successive file pages to client memory at full disk speed, with time for the client to do some computing on each sector; thus with a few sectors of buffering the entire disk can be scanned at disk speed. This facility has been used to write a variety of applications, ranging from a scavenger that reconstructs a broken file system, to programs that search files for substrings that match a pattern. The stream level of the file system can read or write n bytes to or from client memory; any portions of the n bytes that occupy full disk sectors are transferred at full disk speed. Loaders

程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com