]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - share/doc/papers/sysperf/4.t
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / share / doc / papers / sysperf / 4.t
1 .\" Copyright (c) 1985 The Regents of the University of California.
2 .\" All rights reserved.
3 .\"
4 .\" Redistribution and use in source and binary forms, with or without
5 .\" modification, are permitted provided that the following conditions
6 .\" are met:
7 .\" 1. Redistributions of source code must retain the above copyright
8 .\"    notice, this list of conditions and the following disclaimer.
9 .\" 2. Redistributions in binary form must reproduce the above copyright
10 .\"    notice, this list of conditions and the following disclaimer in the
11 .\"    documentation and/or other materials provided with the distribution.
12 .\" 3. All advertising materials mentioning features or use of this software
13 .\"    must display the following acknowledgement:
14 .\"     This product includes software developed by the University of
15 .\"     California, Berkeley and its contributors.
16 .\" 4. Neither the name of the University nor the names of its contributors
17 .\"    may be used to endorse or promote products derived from this software
18 .\"    without specific prior written permission.
19 .\"
20 .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 .\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 .\" SUCH DAMAGE.
31 .\"
32 .\"     @(#)4.t 5.1 (Berkeley) 4/17/91
33 .\"
34 .\"     $FreeBSD$
35 .\"
36 .ds RH Performance Improvements
37 .NH
38 Performance Improvements
39 .PP
40 This section outlines the changes made to the system
41 since the 4.2BSD distribution.
42 The changes reported here were made in response
43 to the problems described in Section 3.
44 The improvements fall into two major classes;
45 changes to the kernel that are described in this section,
46 and changes to the system libraries and utilities that are
47 described in the following section.
48 .NH 2
49 Performance Improvements in the Kernel
50 .PP
51 Our goal has been to optimize system performance
52 for our general timesharing environment.
53 Since most sites running 4.2BSD have been forced to take
54 advantage of declining
55 memory costs rather than replace their existing machines with
56 ones that are more powerful, we have
57 chosen to optimize running time at the expense of memory.
58 This tradeoff may need to be reconsidered for personal workstations
59 that have smaller memories and higher latency disks.
60 Decreases in the running time of the system may be unnoticeable
61 because of higher paging rates incurred by a larger kernel.
62 Where possible, we have allowed the size of caches to be controlled
63 so that systems with limited memory may reduce them as appropriate.
64 .NH 3
65 Name Cacheing
66 .PP
67 Our initial profiling studies showed that more than one quarter
68 of the time in the system was spent in the
69 pathname translation routine, \fInamei\fP,
70 translating path names to inodes\u\s-21\s0\d\**.
71 .FS
72 \** \u\s-21\s0\d Inode is an abbreviation for ``Index node''.
73 Each file on the system is described by an inode;
74 the inode maintains access permissions, and an array of pointers to
75 the disk blocks that hold the data associated with the file.
76 .FE
77 An inspection of \fInamei\fP shows that
78 it consists of two nested loops.
79 The outer loop is traversed once per pathname component.
80 The inner loop performs a linear search through a directory looking
81 for a particular pathname component.
82 .PP
83 Our first idea was to reduce the number of iterations
84 around the inner loop of \fInamei\fP by observing that many programs
85 step through a directory performing an operation on each entry in turn.
86 To improve performance for processes doing directory scans,
87 the system keeps track of the directory offset of the last component of the
88 most recently translated path name for each process.
89 If the next name the process requests is in the same directory,
90 the search is started from the offset that the previous name was found
91 (instead of from the beginning of the directory).
92 Changing directories invalidates the cache, as
93 does modifying the directory.
94 For programs that step sequentially through a directory with
95 .EQ
96 delim $$
97 .EN
98 $N$ files, search time decreases from $O ( N sup 2 )$ to $O(N)$.
99 .EQ
100 delim off
101 .EN
102 .PP
103 The cost of the cache is about 20 lines of code
104 (about 0.2 kilobytes)
105 and 16 bytes per process, with the cached data
106 stored in a process's \fIuser\fP vector.
107 .PP
108 As a quick benchmark to verify the maximum effectiveness of the
109 cache we ran ``ls \-l''
110 on a directory containing 600 files.
111 Before the per-process cache this command
112 used 22.3 seconds of system time.
113 After adding the cache the program used the same amount
114 of user time, but the system time dropped to 3.3 seconds.
115 .PP
116 This change prompted our rerunning a profiled system
117 on a machine containing the new \fInamei\fP.
118 The results showed that the time in \fInamei\fP
119 dropped by only 2.6 ms/call and
120 still accounted for 36% of the system call time,
121 18% of the kernel, or about 10% of all the machine cycles.
122 This amounted to a drop in system time from 57% to about 55%.
123 The results are shown in Table 9.
124 .KF
125 .DS L
126 .TS
127 center box;
128 l r r.
129 part    time    % of kernel
130 _
131 self    11.0 ms/call    9.2%
132 child   10.6 ms/call    8.9%
133 _
134 total   21.6 ms/call    18.1%
135 .TE
136 .ce
137 Table 9. Call times for \fInamei\fP with per-process cache.
138 .DE
139 .KE
140 .PP
141 The small performance improvement
142 was caused by a low cache hit ratio.
143 Although the cache was 90% effective when hit,
144 it was only usable on about 25% of the names being translated.
145 An additional reason for the small improvement was that
146 although the amount of time spent in \fInamei\fP itself
147 decreased substantially,
148 more time was spent in the routines that it called
149 since each directory had to be accessed twice;
150 once to search from the middle to the end,
151 and once to search from the beginning to the middle.
152 .PP
153 Frequent requests for a small set of names are best handled
154 with a cache of recent name translations\**.
155 .FS
156 \** The cache is keyed on a name and the
157 inode and device number of the directory that contains it.
158 Associated with each entry is a pointer to the corresponding
159 entry in the inode table.
160 .FE
161 This has the effect of eliminating the inner loop of \fInamei\fP.
162 For each path name component,
163 \fInamei\fP first looks in its cache of recent translations
164 for the needed name.
165 If it exists, the directory search can be completely eliminated.
166 .PP
167 The system already maintained a cache of recently accessed inodes,
168 so the initial name cache
169 maintained a simple name-inode association that was used to
170 check each component of a path name during name translations.
171 We considered implementing the cache by tagging each inode
172 with its most recently translated name,
173 but eventually decided to have a separate data structure that
174 kept names with pointers to the inode table.
175 Tagging inodes has two drawbacks;
176 many inodes such as those associated with login ports remain in
177 the inode table for a long period of time, but are never looked
178 up by name.
179 Other inodes, such as those describing directories are looked up
180 frequently by many different names (\fIe.g.\fP ``..'').
181 By keeping a separate table of names, the cache can
182 truly reflect the most recently used names.
183 An added benefit is that the table can be sized independently
184 of the inode table, so that machines with small amounts of memory
185 can reduce the size of the cache (or even eliminate it)
186 without modifying the inode table structure.
187 .PP
188 Another issue to be considered is how the name cache should
189 hold references to the inode table.
190 Normally processes hold ``hard references'' by incrementing the
191 reference count in the inode they reference.
192 Since the system reuses only inodes with zero reference counts,
193 a hard reference insures that the inode pointer will remain valid.
194 However, if the name cache holds hard references,
195 it is limited to some fraction of the size of the inode table,
196 since some inodes must be left free for new files.
197 It also makes it impossible for other parts of the kernel
198 to verify sole use of a device or file.
199 These reasons made it impractical to use hard references
200 without affecting the behavior of the inode caching scheme.
201 Thus, we chose instead to keep ``soft references'' protected
202 by a \fIcapability\fP \- a 32-bit number
203 guaranteed to be unique\u\s-22\s0\d \**.
204 .FS
205 \** \u\s-22\s0\d When all the numbers have been exhausted, all outstanding
206 capabilities are purged and numbering starts over from scratch.
207 Purging is possible as all capabilities are easily found in kernel memory.
208 .FE
209 When an entry is made in the name cache,
210 the capability of its inode is copied to the name cache entry.
211 When an inode is reused it is issued a new capability.
212 When a name cache hit occurs,
213 the capability of the name cache entry is compared
214 with the capability of the inode that it references.
215 If the capabilities do not match, the name cache entry is invalid.
216 Since the name cache holds only soft references,
217 it may be sized independent of the size of the inode table.
218 A final benefit of using capabilities is that all
219 cached names for an inode may be invalidated without
220 searching through the entire cache;
221 instead all you need to do is assign a new capability to the inode.
222 .PP
223 The cost of the name cache is about 200 lines of code
224 (about 1.2 kilobytes)
225 and 48 bytes per cache entry.
226 Depending on the size of the system,
227 about 200 to 1000 entries will normally be configured,
228 using 10-50 kilobytes of physical memory.
229 The name cache is resident in memory at all times.
230 .PP
231 After adding the system wide name cache we reran ``ls \-l''
232 on the same directory.
233 The user time remained the same,
234 however the system time rose slightly to 3.7 seconds.
235 This was not surprising as \fInamei\fP
236 now had to maintain the cache,
237 but was never able to make any use of it.
238 .PP
239 Another profiled system was created and measurements
240 were collected over a 17 hour period.  These measurements
241 showed a 13 ms/call decrease in \fInamei\fP, with
242 \fInamei\fP accounting for only 26% of the system call time,
243 13% of the time in the kernel,
244 or about 7% of all the machine cycles.
245 System time dropped from 55% to about 49%.
246 The results are shown in Table 10.
247 .KF
248 .DS L
249 .TS
250 center box;
251 l r r.
252 part    time    % of kernel
253 _
254 self    4.2 ms/call     6.2%
255 child   4.4 ms/call     6.6%
256 _
257 total   8.6 ms/call     12.8%
258 .TE
259 .ce
260 Table 10.  Call times for \fInamei\fP with both caches.
261 .DE
262 .KE
263 .PP
264 On our general time sharing systems we find that during the twelve
265 hour period from 8AM to 8PM the system does 500,000 to 1,000,000
266 name translations.
267 Statistics on the performance of both caches show that
268 the large performance improvement is
269 caused by the high hit ratio.
270 The name cache has a hit rate of 70%-80%;
271 the directory offset cache gets a hit rate of 5%-15%.
272 The combined hit rate of the two caches almost always adds up to 85%.
273 With the addition of the two caches,
274 the percentage of system time devoted to name translation has
275 dropped from 25% to less than 13%.
276 While the system wide cache reduces both the amount of time in
277 the routines that \fInamei\fP calls as well as \fInamei\fP itself
278 (since fewer directories need to be accessed or searched),
279 it is interesting to note that the actual percentage of system
280 time spent in \fInamei\fP itself increases even though the
281 actual time per call decreases.
282 This is because less total time is being spent in the kernel,
283 hence a smaller absolute time becomes a larger total percentage.
284 .NH 3
285 Intelligent Auto Siloing
286 .PP
287 Most terminal input hardware can run in two modes:
288 it can either generate an interrupt each time a character is received,
289 or collect characters in a silo that the system then periodically drains.
290 To provide quick response for interactive input and flow control,
291 a silo must be checked 30 to 50 times per second.
292 Ascii terminals normally exhibit
293 an input rate of less than 30 characters per second.
294 At this input rate
295 they are most efficiently handled with interrupt per character mode,
296 since this generates fewer interrupts than draining the input silos
297 of the terminal multiplexors at each clock interrupt.
298 When input is being generated by another machine
299 or a malfunctioning terminal connection, however,
300 the input rate is usually more than 50 characters per second.
301 It is more efficient to use a device's silo input mode,
302 since this generates fewer interrupts than handling each character
303 as a separate interrupt.
304 Since a given dialup port may switch between uucp logins and user logins,
305 it is impossible to statically select the most efficient input mode to use.
306 .PP
307 We therefore changed the terminal multiplexor handlers
308 to dynamically choose between the use of the silo and the use of
309 per-character interrupts.
310 At low input rates the handler processes characters on an
311 interrupt basis, avoiding the overhead
312 of checking each interface on each clock interrupt.
313 During periods of sustained input, the handler enables the silo
314 and starts a timer to drain input.
315 This timer runs less frequently than the clock interrupts,
316 and is used only when there is a substantial amount of input.
317 The transition from using silos to an interrupt per character is
318 damped to minimize the number of transitions with bursty traffic
319 (such as in network communication).
320 Input characters serve to flush the silo, preventing long latency.
321 By switching between these two modes of operation dynamically,
322 the overhead of checking the silos is incurred only
323 when necessary.
324 .PP
325 In addition to the savings in the terminal handlers,
326 the clock interrupt routine is no longer required to schedule
327 a software interrupt after each hardware interrupt to drain the silos.
328 The software-interrupt level portion of the clock routine is only
329 needed when timers expire or the current user process is collecting
330 an execution profile.
331 Thus, the number of interrupts attributable to clock processing
332 is substantially reduced.
333 .NH 3
334 Process Table Management
335 .PP
336 As systems have grown larger, the size of the process table
337 has grown far past 200 entries.
338 With large tables, linear searches must be eliminated
339 from any frequently used facility.
340 The kernel process table is now multi-threaded to allow selective searching
341 of active and zombie processes.
342 A third list threads unused process table slots.
343 Free slots can be obtained in constant time by taking one
344 from the front of the free list.
345 The number of processes used by a given user may be computed by scanning
346 only the active list.
347 Since the 4.2BSD release,
348 the kernel maintained linked lists of the descendents of each process.
349 This linkage is now exploited when dealing with process exit;
350 parents seeking the exit status of children now avoid linear search
351 of the process table, but examine only their direct descendents.
352 In addition, the previous algorithm for finding all descendents of an exiting
353 process used multiple linear scans of the process table.
354 This has been changed to follow the links between child process and siblings.
355 .PP
356 When forking a new process,
357 the system must assign it a unique process identifier.
358 The system previously scanned the entire process table each time it created
359 a new process to locate an identifier that was not already in use.
360 Now, to avoid scanning the process table for each new process,
361 the system computes a range of unused identifiers
362 that can be directly assigned.
363 Only when the set of identifiers is exhausted is another process table
364 scan required.
365 .NH 3
366 Scheduling
367 .PP
368 Previously the scheduler scanned the entire process table
369 once per second to recompute process priorities.
370 Processes that had run for their entire time slice had their
371 priority lowered.
372 Processes that had not used their time slice, or that had
373 been sleeping for the past second had their priority raised.
374 On systems running many processes,
375 the scheduler represented nearly 20% of the system time.
376 To reduce this overhead,
377 the scheduler has been changed to consider only
378 runnable processes when recomputing priorities.
379 To insure that processes sleeping for more than a second
380 still get their appropriate priority boost,
381 their priority is recomputed when they are placed back on the run queue.
382 Since the set of runnable process is typically only a small fraction
383 of the total number of processes on the system,
384 the cost of invoking the scheduler drops proportionally.
385 .NH 3
386 Clock Handling
387 .PP
388 The hardware clock interrupts the processor 100 times per second
389 at high priority.
390 As most of the clock-based events need not be done at high priority,
391 the system schedules a lower priority software interrupt to do the less
392 time-critical events such as cpu scheduling and timeout processing.
393 Often there are no such events, and the software interrupt handler
394 finds nothing to do and returns.
395 The high priority event now checks to see if there are low priority
396 events to process;
397 if there is nothing to do, the software interrupt is not requested.
398 Often, the high priority interrupt occurs during a period when the
399 machine had been running at low priority.
400 Rather than posting a software interrupt that would occur as
401 soon as it returns,
402 the hardware clock interrupt handler simply lowers the processor priority
403 and calls the software clock routines directly.
404 Between these two optimizations, nearly 80 of the 100 software
405 interrupts per second can be eliminated.
406 .NH 3
407 File System
408 .PP
409 The file system uses a large block size, typically 4096 or 8192 bytes.
410 To allow small files to be stored efficiently, the large blocks can
411 be broken into smaller fragments, typically multiples of 1024 bytes.
412 To minimize the number of full-sized blocks that must be broken
413 into fragments, the file system uses a best fit strategy.
414 Programs that slowly grow files using write of 1024 bytes or less
415 can force the file system to copy the data to
416 successively larger and larger fragments until it finally
417 grows to a full sized block.
418 The file system still uses a best fit strategy the first time
419 a fragment is written.
420 However, the first time that the file system is forced to copy a growing
421 fragment it places it at the beginning of a full sized block.
422 Continued growth can be accommodated without further copying
423 by using up the rest of the block.
424 If the file ceases to grow, the rest of the block is still
425 available for holding other fragments.
426 .PP
427 When creating a new file name,
428 the entire directory in which it will reside must be scanned
429 to insure that the name does not already exist.
430 For large directories, this scan is time consuming.
431 Because there was no provision for shortening directories,
432 a directory that is once over-filled will increase the cost
433 of file creation even after the over-filling is corrected.
434 Thus, for example, a congested uucp connection can leave a legacy long
435 after it is cleared up.
436 To alleviate the problem, the system now deletes empty blocks
437 that it finds at the end of a directory while doing a complete
438 scan to create a new name.
439 .NH 3
440 Network
441 .PP
442 The default amount of buffer space allocated for stream sockets (including
443 pipes) has been increased to 4096 bytes.
444 Stream sockets and pipes now return their buffer sizes in the block size field
445 of the stat structure.
446 This information allows the standard I/O library to use more optimal buffering.
447 Unix domain stream sockets also return a dummy device and inode number
448 in the stat structure to increase compatibility
449 with other pipe implementations.
450 The TCP maximum segment size is calculated according to the destination
451 and interface in use; non-local connections use a more conservative size
452 for long-haul networks.
453 .PP
454 On multiply-homed hosts, the local address bound by TCP now always corresponds
455 to the interface that will be used in transmitting data packets for the
456 connection.
457 Several bugs in the calculation of round trip timing have been corrected.
458 TCP now switches to an alternate gateway when an existing route fails,
459 or when an ICMP redirect message is received.
460 ICMP source quench messages are used to throttle the transmission
461 rate of TCP streams by temporarily creating an artificially small
462 send window, and retransmissions send only a single packet
463 rather than resending all queued data.
464 A send policy has been implemented
465 that decreases the number of small packets outstanding
466 for network terminal traffic [Nagle84],
467 providing additional reduction of network congestion.
468 The overhead of packet routing has been decreased by changes in the routing
469 code and by caching the most recently used route for each datagram socket.
470 .PP
471 The buffer management strategy implemented by \fIsosend\fP has been
472 changed to make better use of the increased size of the socket buffers
473 and a better tuned delayed acknowledgement algorithm.
474 Routing has been modified to include a one element cache of the last
475 route computed.
476 Multiple messages send with the same destination now require less processing.
477 Performance deteriorates because of load in
478 either the sender host, receiver host, or ether.
479 Also, any CPU contention degrades substantially
480 the throughput achievable by user processes [Cabrera85].
481 We have observed empty VAX 11/750s using up to 90% of their cycles
482 transmitting network messages.
483 .NH 3
484 Exec
485 .PP
486 When \fIexec\fP-ing a new process, the kernel creates the new
487 program's argument list by copying the arguments and environment
488 from the parent process's address space into the system, then back out
489 again onto the stack of the newly created process.
490 These two copy operations were done one byte at a time, but
491 are now done a string at a time.
492 This optimization reduced the time to process
493 an argument list by a factor of ten;
494 the average time to do an \fIexec\fP call decreased by 25%.
495 .NH 3
496 Context Switching
497 .PP
498 The kernel used to post a software event when it wanted to force
499 a process to be rescheduled.
500 Often the process would be rescheduled for other reasons before
501 exiting the kernel, delaying the event trap.
502 At some later time the process would again
503 be selected to run and would complete its pending system call,
504 finally causing the event to take place.
505 The event would cause the scheduler to be invoked a second time
506 selecting the same process to run.
507 The fix to this problem is to cancel any software reschedule
508 events when saving a process context.
509 This change doubles the speed with which processes
510 can synchronize using pipes or signals.
511 .NH 3
512 Setjmp/Longjmp
513 .PP
514 The kernel routine \fIsetjmp\fP, that saves the current system
515 context in preparation for a non-local goto used to save many more
516 registers than necessary under most circumstances.
517 By trimming its operation to save only the minimum state required,
518 the overhead for system calls decreased by an average of 13%.
519 .NH 3
520 Compensating for Lack of Compiler Technology
521 .PP
522 The current compilers available for C do not
523 do any significant optimization.
524 Good optimizing compilers are unlikely to be built;
525 the C language is not well suited to optimization
526 because of its rampant use of unbound pointers.
527 Thus, many classical optimizations such as common subexpression
528 analysis and selection of register variables must be done
529 by hand using ``exterior'' knowledge of when such optimizations are safe.
530 .PP
531 Another optimization usually done by optimizing compilers
532 is inline expansion of small or frequently used routines.
533 In past Berkeley systems this has been done by using \fIsed\fP to
534 run over the assembly language and replace calls to small
535 routines with the code for the body of the routine, often
536 a single VAX instruction.
537 While this optimization eliminated the cost of the subroutine
538 call and return,
539 it did not eliminate the pushing and popping of several arguments
540 to the routine.
541 The \fIsed\fP script has been replaced by a more intelligent expander,
542 \fIinline\fP, that merges the pushes and pops into moves to registers.
543 For example, if the C code
544 .DS
545 if (scanc(map[i], 1, 47, i - 63))
546 .DE
547 is compiled into assembly language it generates the code shown
548 in the left hand column of Table 11.
549 The \fIsed\fP inline expander changes this code to that
550 shown in the middle column.
551 The newer optimizer eliminates most of the stack
552 operations to generate the code shown in the right hand column.
553 .KF
554 .TS
555 center, box;
556 c s s s s s
557 c s | c s | c s
558 l l | l l | l l.
559 Alternative C Language Code Optimizations
560 _
561 cc      sed     inline
562 _
563 subl3   $64,_i,\-(sp)   subl3   $64,_i,\-(sp)   subl3   $64,_i,r5
564 pushl   $47     pushl   $47     movl    $47,r4
565 pushl   $1      pushl   $1      pushl   $1
566 mull2   $16,_i,r3       mull2   $16,_i,r3       mull2   $16,_i,r3
567 pushl   \-56(fp)[r3]    pushl   \-56(fp)[r3]    movl    \-56(fp)[r3],r2
568 calls   $4,_scanc       movl    (sp)+,r5        movl    (sp)+,r3
569 tstl    r0      movl    (sp)+,r4        scanc   r2,(r3),(r4),r5
570 jeql    L7      movl    (sp)+,r3        tstl    r0
571                 movl    (sp)+,r2        jeql    L7
572                 scanc   r2,(r3),(r4),r5
573                 tstl    r0
574                 jeql    L7
575 .TE
576 .ce
577 Table 11. Alternative inline code expansions.
578 .KE
579 .PP
580 Another optimization involved reevaluating
581 existing data structures in the context of the current system.
582 For example, disk buffer hashing was implemented when the system
583 typically had thirty to fifty buffers.
584 Most systems today have 200 to 1000 buffers.
585 Consequently, most of the hash chains contained
586 ten to a hundred buffers each!
587 The running time of the low level buffer management primitives was
588 dramatically improved simply by enlarging the size of the hash table.
589 .NH 2
590 Improvements to Libraries and Utilities
591 .PP
592 Intuitively, changes to the kernel would seem to have the greatest
593 payoff since they affect all programs that run on the system.
594 However, the kernel has been tuned many times before, so the
595 opportunity for significant improvement was small.
596 By contrast, many of the libraries and utilities had never been tuned.
597 For example, we found utilities that spent 90% of their
598 running time doing single character read system calls.
599 Changing the utility to use the standard I/O library cut the
600 running time by a factor of five!
601 Thus, while most of our time has been spent tuning the kernel,
602 more than half of the speedups are because of improvements in
603 other parts of the system.
604 Some of the more dramatic changes are described in the following
605 subsections.
606 .NH 3
607 Hashed Databases
608 .PP
609 UNIX provides a set of database management routines, \fIdbm\fP,
610 that can be used to speed lookups in large data files
611 with an external hashed index file.
612 The original version of dbm was designed to work with only one
613 database at a time.  These routines were generalized to handle
614 multiple database files, enabling them to be used in rewrites
615 of the password and host file lookup routines.  The new routines
616 used to access the password file significantly improve the running
617 time of many important programs such as the mail subsystem,
618 the C-shell (in doing tilde expansion), \fIls \-l\fP, etc.
619 .NH 3
620 Buffered I/O
621 .PP
622 The new filesystem with its larger block sizes allows better
623 performance, but it is possible to degrade system performance
624 by performing numerous small transfers rather than using
625 appropriately-sized buffers.
626 The standard I/O library
627 automatically determines the optimal buffer size for each file.
628 Some C library routines and commonly-used programs use low-level
629 I/O or their own buffering, however.
630 Several important utilities that did not use the standard I/O library
631 and were buffering I/O using the old optimal buffer size,
632 1Kbytes; the programs were changed to buffer I/O according to the
633 optimal file system blocksize.
634 These include the editor, the assembler, loader, remote file copy,
635 the text formatting programs, and the C compiler.
636 .PP
637 The standard error output has traditionally been unbuffered
638 to prevent delay in presenting the output to the user,
639 and to prevent it from being lost if buffers are not flushed.
640 The inordinate expense of sending single-byte packets through
641 the network led us to impose a buffering scheme on the standard
642 error stream.
643 Within a single call to \fIfprintf\fP, all output is buffered temporarily.
644 Before the call returns, all output is flushed and the stream is again
645 marked unbuffered.
646 As before, the normal block or line buffering mechanisms can be used
647 instead of the default behavior.
648 .PP
649 It is possible for programs with good intentions to unintentionally
650 defeat the standard I/O library's choice of I/O buffer size by using
651 the \fIsetbuf\fP call to assign an output buffer.
652 Because of portability requirements, the default buffer size provided
653 by \fIsetbuf\fP is 1024 bytes; this can lead, once again, to added
654 overhead.
655 One such program with this problem was \fIcat\fP;
656 there are undoubtedly other standard system utilities with similar problems
657 as the system has changed much since they were originally written.
658 .NH 3
659 Mail System
660 .PP
661 The problems discussed in section 3.1.1 prompted significant work
662 on the entire mail system.  The first problem identified was a bug
663 in the \fIsyslog\fP program.  The mail delivery program, \fIsendmail\fP
664 logs all mail transactions through this process with the 4.2BSD interprocess
665 communication facilities.  \fISyslog\fP then records the information in
666 a log file.  Unfortunately, \fIsyslog\fP was performing a \fIsync\fP
667 operation after each message it received, whether it was logged to a file
668 or not.  This wreaked havoc on the effectiveness of the
669 buffer cache and explained, to a large
670 extent, why sending mail to large distribution lists generated such a
671 heavy load on the system (one syslog message was generated for each
672 message recipient causing almost a continuous sequence of sync operations).
673 .PP
674 The hashed data base files were
675 installed in all mail programs, resulting in an order of magnitude
676 speedup on large distribution lists.  The code in \fI/bin/mail\fP
677 that notifies the \fIcomsat\fP program when mail has been delivered to
678 a user was changed to cache host table lookups, resulting in a similar
679 speedup on large distribution lists.
680 .PP
681 Next, the file locking facilities
682 provided in 4.2BSD, \fIflock\fP\|(2), were used in place of the old
683 locking mechanism.
684 The mail system previously used \fIlink\fP and \fIunlink\fP in
685 implementing file locking primitives.
686 Because these operations usually modify the contents of directories
687 they require synchronous disk operations and cannot take
688 advantage of the name cache maintained by the system.
689 Unlink requires that the entry be found in the directory so that
690 it can be removed;
691 link requires that the directory be scanned to insure that the name
692 does not already exist.
693 By contrast the advisory locking facility in 4.2BSD is
694 efficient because it is all done with in-memory tables.
695 Thus, the mail system was modified to use the file locking primitives.
696 This yielded another 10% cut in the basic overhead of delivering mail.
697 Extensive profiling and tuning of \fIsendmail\fP and
698 compiling it without debugging code reduced the overhead by another 20%.
699 .NH 3
700 Network Servers
701 .PP
702 With the introduction of the network facilities in 4.2BSD,
703 a myriad of services became available, each of which
704 required its own daemon process.
705 Many of these daemons were rarely if ever used,
706 yet they lay asleep in the process table consuming
707 system resources and generally slowing down response.
708 Rather than having many servers started at boot time, a single server,
709 \fIinetd\fP was substituted.
710 This process reads a simple configuration file
711 that specifies the services the system is willing to support
712 and listens for service requests on each service's Internet port.
713 When a client requests service the appropriate server is created
714 and passed a service connection as its standard input.  Servers
715 that require the identity of their client may use the \fIgetpeername\fP
716 system call; likewise \fIgetsockname\fP may be used to find out
717 a server's local address without consulting data base files.
718 This scheme is attractive for several reasons:
719 .IP \(bu 3
720 it eliminates
721 as many as a dozen processes, easing system overhead and
722 allowing the file and text tables to be made smaller,
723 .IP \(bu 3
724 servers need not contain the code required to handle connection
725 queueing, simplifying the programs, and
726 .IP \(bu 3
727 installing and replacing servers becomes simpler.
728 .PP
729 With an increased numbers of networks, both local and external to Berkeley,
730 we found that the overhead of the routing process was becoming
731 inordinately high.
732 Several changes were made in the routing daemon to reduce this load.
733 Routes to external networks are no longer exchanged by routers
734 on the internal machines, only a route to a default gateway.
735 This reduces the amount of network traffic and the time required
736 to process routing messages.
737 In addition, the routing daemon was profiled
738 and functions responsible for large amounts
739 of time were optimized.
740 The major changes were a faster hashing scheme,
741 and inline expansions of the ubiquitous byte-swapping functions.
742 .PP
743 Under certain circumstances, when output was blocked,
744 attempts by the remote login process
745 to send output to the user were rejected by the system,
746 although a prior \fIselect\fP call had indicated that data could be sent.
747 This resulted in continuous attempts to write the data until the remote
748 user restarted output.
749 This problem was initially avoided in the remote login handler,
750 and the original problem in the kernel has since been corrected.
751 .NH 3
752 The C Run-time Library
753 .PP
754 Several people have found poorly tuned code
755 in frequently used routines in the C library [Lankford84].
756 In particular the running time of the string routines can be
757 cut in half by rewriting them using the VAX string instructions.
758 The memory allocation routines have been tuned to waste less
759 memory for memory allocations with sizes that are a power of two.
760 Certain library routines that did file input in one-character reads
761 have been corrected.
762 Other library routines including \fIfread\fP and \fIfwrite\fP
763 have been rewritten for efficiency.
764 .NH 3
765 Csh
766 .PP
767 The C-shell was converted to run on 4.2BSD by
768 writing a set of routines to simulate the old jobs library.
769 While this provided a functioning C-shell,
770 it was grossly inefficient, generating up
771 to twenty system calls per prompt.
772 The C-shell has been modified to use the new signal
773 facilities directly,
774 cutting the number of system calls per prompt in half.
775 Additional tuning was done with the help of profiling
776 to cut the cost of frequently used facilities.