The filesystem waits for the disk instead of asking it repeatedly
sbfsWaitDisk spun on the status port. Read ahead had already hidden about
three quarters of the latency, and what remained was still 11.5% of Type
over a 14K file on a ten thousand cycle disk - all of it memory traffic
spent finding out that nothing had happened yet.
It tests the port first and only waits if the disk is still busy, and that
order is the whole of what makes it safe: the disk raises its line when it
finishes, so a disk that finished in the gap between the test and the WAIT
has its line standing already and the WAIT does nothing rather than
sleeping through the answer. No handler and no vector - the shell keeps the
Interrupt Flag down, and a WAIT wakes on a line whether or not anybody
means to answer it.
Printing a 14K file, by where the cycles go:
cycles a block total bus waiting
0 922,570 922,570 0
2,000 946,474 922,702 23,772
10,000 1,042,474 922,702 119,772
The bus column stops moving. What the program costs in memory is now the
same whatever the disk does. On this emulator nothing observable changes;
on hardware it is a CPU standing out of the way of the memory controller
rather than competing with it for every one of those 119,772 cycles.
The first version cost 660 cycles more at latency zero because it read the
status port again on the way out. AND writes to Q and leaves A alone, so
the status was already there - which is what the original said in its own
comment, and what I stopped believing while rewriting around it.
This commit is contained in:
@@ -776,6 +776,33 @@ cent, where without it the same change costs sixty eight - which is the point: *
|
||||
that reads ahead stops caring very much how fast its disk is.** The three per cent it costs
|
||||
at zero is the bookkeeping, paid when there is nothing to hide behind it.
|
||||
|
||||
### And Waiting For What Is Left:
|
||||
|
||||
Read ahead hides most of the wait and cannot hide all of it. What remained was a loop
|
||||
asking the disk's status port over and over, which is work the machine is doing and memory
|
||||
it is touching to find out that nothing has happened yet.
|
||||
|
||||
`sbfsWaitDisk` uses `WAIT` now. It tests the status port, and only if the disk is still
|
||||
busy does it stop - the CPU is put down until a device raises a line, and the disk raises
|
||||
one when it finishes. **Asking first is what makes it safe:** if the disk finished in the
|
||||
gap between the test and the `WAIT`, its line is already standing and the `WAIT` does
|
||||
nothing rather than sleeping through the answer.
|
||||
|
||||
There is no handler and no vector. The shell keeps the Interrupt Flag down, and a `WAIT`
|
||||
wakes on a line whether or not anybody intends to answer it, taking it down on the way
|
||||
past. Printing the same fourteen kilobyte file:
|
||||
|
||||
| Cycles a block | Total | Of that, the bus | Waiting |
|
||||
| -- | -- | -- | -- |
|
||||
| 0 | 922,570 | 922,570 | 0 |
|
||||
| 2,000 | 946,474 | 922,702 | 23,772 |
|
||||
| 10,000 | 1,042,474 | 922,702 | 119,772 |
|
||||
|
||||
**The middle column stops moving.** What the program costs in memory is now the same
|
||||
whatever the disk does, and the difference is time spent with the bus quiet. On this
|
||||
emulator that changes nothing anybody can see; on hardware it is the difference between a
|
||||
CPU contending for memory with everything else and a CPU standing out of the way.
|
||||
|
||||
### Saving Something Twice:
|
||||
|
||||
Which is why saving a document is not the same as writing a file, and why sbfsSaveFile exists rather than each tool doing it. A file that has grown will usually not fit where it was, so saving it means putting it somewhere else and letting go of where it was - and **the obvious order is a trap**:
|
||||
|
||||
Reference in New Issue
Block a user