Jan 12th, 2015
by Xinran Chen, Ivy Wang and Yingjia Lee
Our original program worked by first reading in input, waiting for the reading to be done and processing the read input, and repeating. Double buffering works by overlapping the reading and processing tasks so that they are done concurrently. In the word count program, processing time is significantly shorter than the reading time so the result of double buffering would be that our program is always reading.
If our program required more processing, for example, if we had to decrypt the input, double buffering would allow the program to read in the next block of input while it was decrypting the input from the last block that was read
Double buffering works best when the reading and processing time take about the same because there would be less time spent waiting for the process to finish so that the next block of input could be dealt with.
Increasing the buffer size to (say) 128 sectors would decrease the rotational latency time (the time taken to bring the correct disk sector under the read-write head of the hard disk)
Our original program uses the CPU's programed input/output (PIO) to read and write from hard disk. This approach is slow because it takes up unnecessary time when the data needs to be sent through the CPU for reading and writing to be done.
Direct Memory Access allows a device to read directly to memory so that unnecessary time spent in the CPU is avoided.
In order to update the read/write process, we need to optimize all three of these. Ideally, we'll only have one copy of a read/write function. Theoretically, this could be done by asking the company that makes the EEPROM to accommodate a read_ide_sector to support DMA and read larger sectors.
We would then have every program use that program in BIOS. In fact, this was what BIOS was originally intended for.
There is a standard set of locations for functions in the BIOS and it's a pain to conform to what the BIOS allows.
There is a lack of flexibility in what the BIOS allows, and asking for changes by contacting the company that makes the EEPROM is unreasonable.
Our program will not scale very well: There will be more bugs as the program gets bigger and the large amount of code will become harder to debug
We can't reuse parts of the word count program that is applicable to other programs (like read_ide_sector) because it would mean that we wouldn't be able to make changes to the wc program without worrying how it affects other programs
It's hard for the word count program to recover from faults
We can't run multiple applications simultaneously.
* Waterbed Effect: Optimizing one metric often causes the other metrics to suffer. We need to determine whether or not these tradeoffs are worth it.
void read_ide_sector(int s, int a);
void read_sector(int diskno, int s, int a);
It no longer assumes that there's only one disk.
int read_sector(int diskno, int s, int a);
The original function wasn't as reliable as we wouldn't have known if it failed to work.
void read_sector(int diskno, int s, int a, int nsecs);
We no longer assume that we only want to read off 1 sector at a time.
void read_sector(int diskno, int bytesoffset, int a, int nbytes);
Instead of assuming that 512 bytes is the sector size, we can just specify a byte offset and the number of bytes that we want to read.
function call modularity: we break the program up into functions and then call the functions
How function call modularity works in recursion example:
int fact (int n) {
return n ? n*fact(n-1) : 1;
//if n is zero, it'll return 1, else call (n-1)
//if we put in -1, it'll run till we get to ready only
// and it'll crash
//we would only be able to compute ~20 on x-86
}
fact: pushl %ebp
movl $1, %eax
movl %esp, %ebp
subl $8, %esp
movl %ebx, -4(%ebp)
movl 8(%ebp), %ebx
testl %ebx, %ebx
jne .L5
.L1: movl -4(%ebp), %ebx
movl %ebp, %esp
popl %ebp
ret
.L5 leal -1(%ebx), %eax
movl %eax, (%esp)
call fact
imull %ebx, %eax
jmp .L1
We can trace through this in gdb using step instructions.