Writing Disk Driver
>>>By the way, I contacted Developer's Support on this issue any their response was,
>>>we knew the file system was slow however we didn't realize it was that slow!
Who told you that? I work in Apple's Developer Technical Support
group and provide primary support for the Macintosh file system and I
know I didn't give you that answer. If you need more help and are an
Apple Partner, write to DevSupport again and put "Attn: Jim Luther"
in the subject.
There are several easy ways to speed up your file system I/O. I know
because I'm scheduled to give a session on that subject at this
year's Worldwide Developer Conference in May. Here's the top ways
(going from biggest win to smallest):
1) increase the size of your I/O operations. 32K reads or writes will
get you above the knee of the performance curve on most devices
except for older floppy disks and 150K per second CD-ROM drives. 64K
is about right for best CD-ROM performance.
2) Align your read and write operations to 512-byte boundaries and
make your requests in multiples of 512-bytes.
3) Use the file system cache wisely. The Macintosh file system uses a
simple block cache mechanism that seems to have been designed mostly
to make writing the HFS file system easy. It handles random block
reads and writes very nicely. However, most programs I've seen
either read/write a file in one big I/O operation or somewhat
sequentially in random sized chunks and rarely do they use the same
data more than once in a short period of time (over longer periods,
it's very likely that the data will be flushed out of the cache).
So, if you don't need your data cached, set the noCacheBit in your
I/O operations. You'll speed up the whole ssystem since youre reads
and writes won't be flushing other cached data. Here's the functions
you can use:
pascal OSErr FSReadNoCache(short refNum, long *count, void *buffPtr)
{
ParamBlockRec pb;
OSErr error;
pb.ioParam.ioRefNum = refNum;
pb.ioParam.ioBuffer = buffPtr;
pb.ioParam.ioReqCount = *count;
pb.ioParam.ioPosMode = fsAtMark + 0x0020; /* fsAtMark + noCacheBit */
pb.ioParam.ioPosOffset = 0;
error = PBReadSync(&pb);
*count = pb.ioParam.ioActCount;
return (error);
}
pascal OSErr FSWriteNoCache(short refNum, long *count, const void *buffPtr)
{
ParamBlockRec pb;
OSErr error;
pb.ioParam.ioRefNum = refNum;
pb.ioParam.ioBuffer = (Ptr)buffPtr;
pb.ioParam.ioReqCount = *count;
pb.ioParam.ioPosMode = fsAtMark + 0x0020; /* fsAtMark + noCacheBit */
pb.ioParam.ioPosOffset = 0;
error = PBWriteSync(&pb);
*count = pb.ioParam.ioActCount;
return (error);
}
4) Don't use FlushVol unless you've made a change to the volume you
can see with PBGetCatInfo. Don't use FlushFile unless you've made a
change you can see by reading the file.
5) Preallocate the space for your file in large chunks. You (more
than the file system) probably have a prety good idea how big your
file is going to be before you write it. If you preallocate the
space for the file with SetEOF, the file blocks will be allocated in
large contiguous chunks (probably in one big chunk) and each write
can skip quickly over the code that allocates more space for the
file. Use SetEOF instead of Allocate because it ensures the blocks
are permanently claimed (unused blocks between the logical EOF and
physical EOF are released with the file or volume is flushed).
6) Do your own buffered I/O. Read or write in large chunks and use the
data in the buffer. Again, you *know* how you're going to use the data
– the file system can only make a stupid guess.
7) Use overlapping asynchronous I/O. It doesn't do much now on most
systems, but in the future it will. SCSI Manager 4.3 on the AV
systems now lets you start an asynchronous operation and crunch on
other data while the SCSI system gets the requested data.