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