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