shulman@RED.RUTGERS.EDU (Jeffrey Shulman) (09/25/86)
[ Uploaded from Delphi by Jeff Shulman ] Name: DISKTIMER Date: 18-SEP-1986 06:21 by BRECHER DiskTimer is a hard disk performance benchmark that measures speed of large (32KB) data transfers and of seeking (head movement). It does not alter the contents of the disk, and its results are independent of the Control Panel cache and file system (independent of file fragmentation, free space, System & Finder versions, etc.) Submit results to BRECHER; I will from time to time post the current list of results for various disk makes and models. Note re HyperDrive: disable the HyperDrive cache. The access time test is meaningful only if the Startup drawer is over 1MB in size and is contiguous (grew to 1MB+ before any other drawers were created, or is the only drawer). [Moderator's note: The binary for this program is posted to mod.mac.binaries.] --- /* DiskTimer.c Steve Brecher * * This is an MPW C source file. Set Tabs to 4. * * This program does a performance test on the volume from which it is launched: * Data transfer speed: * 32KB reads * 32KB writes * Access time: * 1 512-byte read from block 0 followed by 1 512-byte read * from offset 1MB (volume must be at least 1MB+512bytes large) * * Each test is performed multiple times. * * The write tests use the data that was previously read, so the test is * non-destructive. * * The predecessor program, DiskBench, did the I/O starting at volume offset 0. * That was unfair if the volume started within 32KB of a cylinder boundary, or * if the first 32KB happened to contain a remapped block or track, or required * controller or driver retries due to a soft error. DiskTimer does a * pre-calibration by doing a smaller number of I/O iterations at various * offsets up to 32KB from the start of the volume, and using the offset which * results in the best time for the reported tests. * * To a avoid constant sector skew between I/Os, which might be unfair * to some drives, there is now a varying delay between I/O requests. The * delay is obtained via a pseudo-random number generator, using the same * seed on each run. * * In order to avoid confusion with results from the previous DiskBench * program, DiskTimer reports results in seconds instead of ticks. */ /* #define Version "1.0" /* 15-Sep-86 */ #define Version "1.1" /* 16-Sep-86 report results in seconds */ #include <Types.h> #include <Strings.h> #include <Resources.h> #include <Quickdraw.h> #include <Fonts.h> #include <Windows.h> #include <TextEdit.h> #include <Dialogs.h> #include <Memory.h> #include <Events.h> #include <Files.h> #include <SegLoad.h> /* If the following two counts are changed, DITL 129 must be changed also */ #define TransferCount 100 /* number of times to perform data transfer tests */ #define SeekCount 80 /* number of times to perform seek test */ #define CalibCount 10 /* number of read iterations for calibration */ #define XferSize (32*1024) #define WaitDlogID 128 #define ResultsAlertID 129 #define ErrorAlertID 130 #define GreetAlertID 131 #define StrVolTooSmall 128 #define CurApRefNum ((short *)0x900) #define FCBsPtr ((Ptr *)0x34E) #define Ticks ((longword *)0x16A) #define fcbVPtr 20 typedef unsigned long longword; DialogPtr DlogPtr; Handle BuffHndl; /* to I/O buffer */ IOParam ioPB; longword XferRdTicks, XferWtTicks, AccessTicks; Boolean SkipAccess; /* flag to skip access tests if volume too small */ longword VolOffset; void Error(err) OSErr err; { char ErrStr[8]; DisposDialog(DlogPtr); sprintf(ErrStr, "%d", err); ParamText(ErrStr, 0, 0, 0); StopAlert(ErrorAlertID, 0); ExitToShell(); } /* * Put up intro alert; return true/false for OK/Cancel. If OK, put up * "Please wait" dialog and get mouse location; get data buffer. * Note: random seed is initted to 1 by InitGraf. */ Boolean Initialize() { extern struct qd qd; InitGraf(&qd.thePort); InitFonts(); InitWindows(); TEInit(); InitDialogs(0); FlushEvents(everyEvent, 0); BuffHndl = NewHandle(XferSize); InitCursor(); if (Alert(GreetAlertID, 0L) == ok) { DlogPtr = GetNewDialog(WaitDlogID, 0, (WindowPtr)-1); BeginUpdate(DlogPtr); DrawDialog(DlogPtr); EndUpdate(DlogPtr); return true; } return false; } longword XferTest(Count, Write) int Count; Boolean Write; { int i; OSErr err; longword StartTicks, TempTicks, CurrTicks; StartTicks = *Ticks; for (i=0; i<Count; ) { ioPB.ioPosOffset = VolOffset; if (Write) err = PBWrite(&ioPB, false); else err = PBRead(&ioPB, false); if (err) Error(err); if (++i < Count) { TempTicks = *Ticks; Delay((Random()&7)+1, &CurrTicks); StartTicks += CurrTicks - TempTicks; } } return *Ticks - StartTicks; } /* * Set up ioPB with driver refNum and default volume's drive number. * Set value of SkipAccess flag. * Determine which offset in the set 0,4K,8K,...32K from start of volume * results in best 32K read time. */ void Calibrate() { VCB *vcbPtr; longword TempTicks, BestTicks, BestOffset; vcbPtr = *(VCB **)(*FCBsPtr + *CurApRefNum + fcbVPtr); ioPB.ioRefNum = vcbPtr->vcbDRefNum; ioPB.ioVRefNum = vcbPtr->vcbDrvNum; ioPB.ioBuffer = *BuffHndl; ioPB.ioPosMode = fsFromStart; ioPB.ioReqCount = 512; VolOffset = 0; XferTest(1, false); /* seek to start of volume */ ioPB.ioReqCount = XferSize; BestTicks = 0xFFFFFFFF; for (VolOffset=0; VolOffset<XferSize; VolOffset+=4096) if ((TempTicks=XferTest(CalibCount, false)) < BestTicks) { BestTicks = TempTicks; BestOffset = VolOffset; } VolOffset = BestOffset; SkipAccess = (vcbPtr->vcbAlBlkSiz * vcbPtr->vcbNmAlBlks) < VolOffset + 1024*1024+512; } longword AccessTest() { int i; OSErr err; longword StartTicks, TempTicks, CurrTicks; ioPB.ioReqCount = 512; StartTicks = *Ticks; for (i=0; i<SeekCount/2; ) { ioPB.ioPosOffset = VolOffset + 1024*1024; if (err = PBRead(&ioPB, false)) Error(err); TempTicks = *Ticks; Delay((Random()&7)+1, &CurrTicks); StartTicks += CurrTicks - TempTicks; ioPB.ioPosOffset = VolOffset; if (err = PBRead(&ioPB, false)) Error(err); if (++i < SeekCount/2) { TempTicks = *Ticks; Delay((Random()&7)+1, &CurrTicks); StartTicks += CurrTicks - TempTicks; } } return *Ticks - StartTicks; } void Results() { char XferRdStr[8], XferWtStr[8], AccessStr[64]; short itemHit; DisposDialog(DlogPtr); /* take down "Please wait" */ sprintf(XferRdStr, "%6.1f", XferRdTicks/60.0); sprintf(XferWtStr, "%6.1f", XferWtTicks/60.0); if (SkipAccess) strcpy(AccessStr, p2cstr(*GetResource('STR ', StrVolTooSmall))); else sprintf(AccessStr, "%6.1f", AccessTicks/60.0); ParamText(XferRdStr, XferWtStr, AccessStr, Version); Alert(ResultsAlertID, 0); } main() { if (Initialize()) { Calibrate(); XferRdTicks = XferTest(TransferCount, false); /* reads */ XferWtTicks = XferTest(TransferCount, true); /* writes */ if (!SkipAccess) AccessTicks = AccessTest(); Results(); } ExitToShell(); } ---