[mod.mac.sources] DiskTimer

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();
}
---