{**********************************} { BYTE Windows Benchmarks } { Version 1.0 } { July 1992 } { Turbo Pascal Version by } { Rick Grehan } { Version 1.1 Revisions by } { Raymond GA Cote } { Appropriate Solutions, Inc. } {**********************************} { HISTORY: 93Jan27 RGAC -- Appropriate Solutions, Inc. Added following changes culminating on this date. 1: -L on command line will cause tests to be run automatically, sent to a log file, and program to terminate. 2: a filename on the command line will be used for logfile, otherwise we will use a default name. 3: Window is now set to 640 by 480. 4: Did not change algorithm in sequential file I/O. Simply limited writing within array bounds and started/stopped timer. 5: Since sfio was setup as a single reportable number I left it that way. 6: System no longer appears to be crashing. I've run it several hours without problems. It still needs, however, error checking and handling routines. 7: Sfile_io test was not set-up as a timed test. Rather it runs once and one-time experiment is used for a timing value. I did not change this. 8: Rfile_io is configured as a timed test. However, I did not alter the user interface to allow timing data to be entered. However, I do use the global variables for timing the test. 9: Random file i/o routine was translated from Byte Low level Benchmarks. It uses a single-character array (it is never changed). This may cause problems with upcoming intelligent cache controllers which may not decide to actually write the information. Needs to be changed in future software release. 10: Lab needs to determine test environment for running windows benchmark. SmartDrive sizes, etc. 11: Random I/O currently set to run for 30 seconds. This produces 965 Kbytes/second on my system which is just slightly slower than the BBENCH low-level throughput value. The following values were received from the test at varying times: 15s 1,421 Kbytes/sec 30s 965 Kbytes/sec 45s 728 Kbytes/sec 60s 610 Kbytes/sec Current interface does not allow time to be set from this user interface. 12: Added a 5-second delay to start of random file i/o test. My reasoning is as follows: Random file i/o runs immediately after the sequential file i/o. System may still be flushing the cache from the sequential test. This will have a dramatic impact on the test results. Specifically. Running the random test by itself will produce different results than whe they are run in a batch. 13: Log file is timestamped and contains its own name. 14: Local Memory test no longer locks up the system. 15: Put local memory test into iteration loop. Did quick check to see that it performed properly. Verified that calcluated output shown in reports is valid. 16: Put global memory test into iteration loop. Reported numbers are very low. Test slows down dramatically between iterations. First iteration takes 1.4 seconds, last iteration takes 6.2 seconds. I've not explored the problem. 17: Fixed improper addition (or nonaddition) of '0' to minutes in timelog. 18: Instead of handling display of external log files, I've allowed you to view results of all tests up to this point. Viewing also logs all test results to the log file. 19: Changed displayBenchLine so it tracks its own position. Added InitBenchDisplay which sets-up the variables. Should be called before each new set of screen displays. Note: you won't get scrolling with these routines. 20: Allow user to enter name of logfile. Currently only accepts eight character name and three-character extension. No error checking done in current code. Caveat Emptor. ============= that's it for now, I'll come back and polish a few things in about a week. == } { Things that still need doing: 1: File I/O error handling. 2: Memory allocation error handling. 3: User interface is much too "cute". 4: Need to follow Windows GUI standards. 5: Still need processor identification. 7: Display system configuration. 10: Final testing. 11: Validation from the field. 13: Determine if global memory test is functioning properly. 14: Determine when window is activated and update the information. 15: Need to not use the Microsoft trademark as minimized icon. } program ByteWbench; {$R BYTEWB } { Our resource file is BYTEWB.RES } {$X+ } { Enable extended syntax } uses WinTypes, WinProcs, Strings, WinDos; (********************** ** GLOBAL CONSTANTS ** **********************) CONST NULL = 0; ID_PIXEL_BUTTON = 101; ID_LINES_BUTTON = 102; ID_RECTS_BUTTON = 103; ID_POLYS_BUTTON = 104; ID_ARCE_BUTTON = 105; ID_BITBLT_BUTTON = 106; ID_LMEM_BUTTON = 107; ID_GMEM_BUTTON = 108; ID_SFIO_BUTTON = 109; ID_RFIO_BUTTON = 110; ID_TEXT_BUTTON = 111; ID_PIXEL_SECS = 120; ID_LINE_SECS = 121; ID_RECT_SECS = 122; ID_POLY_SECS = 123; ID_ARCE_SECS = 124; ID_BITBLT_SECS = 125; ID_LMEM_SECS = 126; ID_GMEM_SECS = 127; ID_TEXT_SECS = 128; IDM_SETLOG = 201; IDM_DISPLOG = 202; IDM_EXIT = 203; IDM_INFO = 204; IDM_COMPINFO = 205; IDM_CONFBENCHES = 211; IDM_SYSCONF = 212; IDM_SETDEFS = 213; IDM_EXECUTE = 221; IDB_OK = 150; IDB_CANCEL = 151; IDL_OK = 101; IDL_CANCEL = 103; IDL_LOGFIELD = 102; (** ** Number of timer array elements. **) time_array_elems = 4; (** ** Minimum number of seconds for tests. ** These define the minimum that the user can set a test ** to, the idea being that anything less makes the results ** questionable. *) min_pixel_secs = 30; min_line_secs = 30; min_rect_secs = 30; min_poly_secs = 30; min_arce_secs = 30; min_bitblt_secs = 30; min_lmem_secs = 30; min_gmem_secs = 30; min_text_secs = 30; min_rfile_io_secs = 30; min_sfile_io_secs = 30; (** ** These constants set the number of "things done" per ** iteration. In a future version, it might be a good ** idea to have these fellows become minimum values and ** let the user adjust the number of repetitions per ** iteration. *) pixels_per_iter =1000; { # of pixels per iteration } lines_per_iter = 1000; { # of lines per iteration } polys_per_iter = 20; { # of poly lines per iteration } rects_per_iter = 1000; { # of rects per iteration } ellps_per_iter = 1000; { # of ellipses per iteration } bmap_points = 1000; { # of bitmap points per iteration } lmems_per_iter = 1; { # of local memory tests per iter } gmems_per_iter = 1; { # of global memory tests per iter } text_per_iter = 400; { # of text strings per iteration } { the following bizarre value comes from the use of a nonBinary-power value as the upper bounds of the sequential I/O test. I've left it here since that is how the code was presented. rgac } sfio_bytes_per_iter = 19995904 * 2; {same # bytes for write and read} disk_space_needed = 6000 * 1024; { 6 MB } bmap_size = 72; { Bitmap x & y dimension } (* ** NOTE: The pre-release version of the benchmarks used ** pen styles other than the ps_Solid. This caused problems ** with some accelerator cards, which had a hard time doing ** ps_Dash and ps_Dot pens. The results were skewed [how ** many times have you seen dashed or dotted lines onscreen?]. ** So, all pens are now ps_Solid. --RG *) pen_style: ARRAY [0..2] OF Integer = ( ps_Solid, ps_Dash, ps_Dot ); (** ** Brush types. ** NOTE: The benchmarks have a larger number of null brushes so ** that they tend to test empy polygons more frequently than ** filled ones. The actual ratio should -- at some time in ** the future -- be determined by profiling. **) brush_style: ARRAY [0..9] OF Integer = ( Black_Brush, Null_Brush, DkGray_Brush, Null_Brush, Gray_Brush, Hollow_Brush, LtGray_Brush, Null_Brush, White_Brush, Null_Brush ); (** ** The following array of edit box id numbers are used ** to facilitate the scanning of edit boxes on the ** configuration scree. **) edit_box_ids: ARRAY[0..8] OF Word = ( ID_PIXEL_SECS, ID_LINE_SECS, ID_RECT_SECS, ID_POLY_SECS, ID_ARCE_SECS, ID_BITBLT_SECS, ID_LMEM_SECS, ID_GMEM_SECS, ID_TEXT_SECS ); (** ** The following array is used to scan edit boxes ** and verify that their contents are above the ** allowed minimum. **) edit_box_mins: ARRAY[0..8] OF Word = ( min_pixel_secs, min_line_secs, min_rect_secs, min_poly_secs, min_arce_secs, min_bitblt_secs, min_lmem_secs, min_gmem_secs, min_text_secs ); (** ** The following array is used to scan the checkbox ** buttons. ** NOTE: It's ordering is important!! It must ** correspond to the following set of constants. ** i.e., if pixel_test_id=1, then the first member ** of the button_id array MUST be ID__PIXEL_BUTTON. **) button_ids: ARRAY[1..11] OF Word = ( ID_PIXEL_BUTTON, ID_LINES_BUTTON, ID_RECTS_BUTTON, ID_POLYS_BUTTON, ID_ARCE_BUTTON, ID_BITBLT_BUTTON, ID_LMEM_BUTTON, ID_GMEM_BUTTON, ID_SFIO_BUTTON, ID_RFIO_BUTTON, ID_TEXT_BUTTON ); (** ** Test ID numbers. **) pixel_test_id = 1; line_test_id = 2; rect_test_id = 3; poly_test_id = 4; arce_test_id = 5; bitblt_test_id = 6; lmem_test_id = 7; gmem_test_id = 8; sfio_test_id = 9; rfio_test_id = 10; texto_test_id = 11; dtext_test_id = 12; (** ** Test id max. Adjust this as you add more tests. **) max_test_id = 12; max_num_tests = max_test_id; (** ** Preferences. This is a section to be expanded in ** the future. **) pref_clipamt_id = 1; { Do clipping } max_pref_id = 1; (** ** Following constant array holds the number of "things per ** iteration" of each test. **) items_per_iter: ARRAY[1..max_test_id] OF LongInt = ( pixels_per_iter, lines_per_iter, rects_per_iter, polys_per_iter, ellps_per_iter, bmap_points, lmems_per_iter, gmems_per_iter, 1, 1, text_per_iter, text_per_iter ); (** ** Following constants set default values for parameter ** of many of the benchmarks. These are initially loaded ** into variables. ** NOTE: Future version of benchmarks should make these ** values user-modifiable. **) { Local memory benchmark } max_local_memitems = 256; max_local_memsize = 30000; max_global_memitems = 512; max_global_memsize = 1000000; (** ** Text strings for displaying results. **) test_name: ARRAY [1..max_test_id] OF String[20] = ( 'Pixels: ', 'Lines: ', 'Rectangles: ', 'Polygons: ', 'Ellipses: ', 'BitBlts: ', 'Loc. Memory: ', 'Glob. Memory: ','Seq. File I/O: ', 'Rand. File I/O: ', 'TextOut: ', 'DrawText: ' ); (** ** Strings used in the TEXT benchmarks. **) bench_strs: ARRAY [0..5] OF String[50] = ( 'The Quick Brown Fox Jumped Over The Lazy Dog', 'Never Eat Anything Bigger Than Your HEAD', 'NEVER hold an electrical wire in your mouth', 'Where Do Pixels Go When Not Being Lit?', 'Do not lean forward as the bus pulls up!', 'Fried Rice should be eaten through the mouth'); DefLogName: String[13] = 'winbench.log'; DefCompName: String[13] = 'wincomp.dat'; { Following used as a cheap source of dashed lines and blanks } dashstr: PChar ='------------------------------------------------------------'#0; blankstr: PChar = ' '#0; { Following defines # of bytes to set aside for each entry in the data comparison file. } compblocksize = 130; (********************** ** TYPE DEFINITIONS ** **********************) TYPE ByteRec = RECORD { Chop a word into 2 bytes } lo, hi: Byte; END; WordRec = RECORD { Chop a LongInt into 2 words } lo, hi: Word; END; BigFileBuffType = ARRAY[0..39999] OF Char; FileName = ARRAY[0..14] OF Char; { The following item, CompData, defines the record layout of each entry in the comparison file. } CompData = RECORD description: ARRAY[0..70] of Char; sysname: ARRAY[0..20] OF Char; testresult: ARRAY[1..max_test_id] OF Real; END; PCompData = ^CompData; (********************** ** GLOBAL VARIABLES ** **********************) VAR pens: ARRAY [0..7] OF HPen; { Handles for pens } stopwatch: Longint; { Timer stopwatch holding var. } (** ** The follow arrays keep track of elapsed time and number ** of iterations for each test. **) elapsedtsecarray: ARRAY[1..max_test_id] OF Longint; iterarray: ARRAY[1..max_test_id] OF Longint; (** ** Next items are special. The text benchmarks actually ** capture separate numbers. One set for TextOut and ** another for DrawText. In the results, the elapsed ** arrays hold the results for DrawText. These next ** variables hold the TextOut results. **) textoutelapsedtsecs: Longint; textoutiters: Longint; randl: ARRAY [0..1] OF LongInt; { Random number storage } HByteWin: HWnd; { Handle to parent window } pstruct: TPaintStruct; { Paint structure } (** ** do_test_flags is an array that shows what tests ** are to be executed. ** tdo_test_flags holds a copy of the above array during ** editing of the configuration information, and allows ** the user to cancel his settings. **) do_test_flags: ARRAY[1..max_test_id] OF Bool; tdo_test_flags: ARRAY[1..max_test_id] OF Bool; pixel_secs: Word; { # of seconds to do pixels test } line_secs: Word; { # of seconds to do lines test } rect_secs: Word; { # of seconds to do rectangles test } poly_secs: Word; { # of seconds to do polygons test } arce_secs: Word; { # of seconds to do arc/ellipse test } bitblt_secs: Word; { # of seconds to do bitblt test } text_secs: Word; { # of seconds to do text test } lmem_secs: Word; { # of seconds to do local memory test } gmem_secs: Word; { # of seconds to do global memory test } rfile_io_secs: Word; { # of seconds to do random file i/o test } sfile_io_secs: Word; { # of seconds to do sequential file i/o test } (** ** Following variables are used in the file I/O ** tests. **) sfilesize: LongInt; { Sequential file size } sfilereclen: ARRAY [0..3] OF Integer; { Record lengths } (** ** Preference settings. **) pref_settings: ARRAY[1..max_pref_id] OF Word; { Following vars. hold stuff for the standard alert } { dialog. If Count<>0, then x,y holds coordinates at } { which to display the text in AlertDlgText. } (** ** Following vars are used to construct an alert dialog ** box...one that just has an "OK" in it. ** If ADTCount<>0, then ADTx and ADTy hold coordinates ** at which to display the text in AlertDlgText. **) ADTx, ADTy: Integer; { Coordinates to display text } ADTCount: Integer; { Text count } AlertDlgText: ARRAY[0..60] of Char; logFileName :String[12]; {Log files are in current directory.} autoLogAndExit: Boolean; {should we automatically write to log file and get out?} logFile : Text; bigFileBuff: ^BigFileBuffType; totalRandomIoBytes: LongInt; {OK, I cheated, it shouldn't be a global.} someTests : Boolean; { have we ever run any tests? } CompFileName: ARRAY[0..14] of Char; { Comparison data file } DataFile: Text; { File holding text comparison data } iscompfilethere: Boolean; { Set to true if comp file is present } {** ** DisplayBenchLine routines. ** Don't physically manipulate these yourself. **} benchLineHDC: HDC; benchLineX: Integer; benchLineY: Integer; benchLineYInc: Integer; benchLineHDCheight: Integer; {** ** Following globals are used in handling the ** benchmark comparison dialog box. **} bcDispRect: TRect; { Display area } bcBeginLine: Integer; { Beginning line to display } bcTotNumLines: Integer; { Total # of lines to display } bcNumLines: Integer; { # of lines in display area } bcNumSystems: Integer; { # of systems in comparison file } sysfontheight: Integer; { Height of system font } ansiffontheight: Integer; { ANSI fixed font height } { Following array of handles is used to point to global memory blocks where we'll store the data read in from the comparison file. } fbhand: ARRAY [1..5] of THandle; { The following pointer leads us to the acutal data stored in the blocks referenced by each member of the fbhand[] array. } fbptr: PCompData; { Pointer to comparison data } oldwinbkcolor: LongInt; { Save window background color } (****************************** ** ++ ROUTINES BEGIN ++ ** ******************************) { ************** } { TIMER ROUTINES } { ************** } { ******************** } { START STOPWATCH } { Starts a stopwatch for benchmarking. } PROCEDURE StartStopWatch; BEGIN stopwatch:=GetTickCount; END; { ***************** } { STOP STOPWATCH } { This procedure turns off the stopwatch, calculates } { elapsed minutes, seconds, and 1/100 seconds and } { places the results in the global stopwatch } { variables. } { Note that this modifies the global elapsedticks } PROCEDURE StopStopWatch; VAR elapsedticks: Longint; BEGIN stopwatch:=GetTickCount-stopwatch; END; (* **************************************** ** Accumulate into a timing array memeber ** Accumulate the elapsed time into a timing array ** entry selected by idx. *) PROCEDURE AccumTiming(idx: Word); BEGIN elapsedtsecarray[idx]:=elapsedtsecarray[idx]+stopwatch; END; { AccumTiming } { *************************************** } { ** RANDOM NUMBER GENERATION ROUTINES ** } { *************************************** } (* ************************** ** Generate a random number ** Second order linear congruential R.N. generator. ** Constants suggested by J.G. Skellam. ** If longval==0, returns next number in sequence. ** If longval!=0, restarts generator. ** NOTE: The generator should be initially started ** with a call to Randnum(13); ** *) FUNCTION Randnum(longval: Longint) : Longint; VAR tlong: Longint; { Temp storage for long integer } BEGIN IF longval<>0 THEN BEGIN randl[0]:=longval; randl[1]:=117; END; tlong:=randl[0]*254754 + randl[1]*529562; tlong:=tlong MOD 999563; randl[1]:=randl[0]; randl[0]:=tlong; Randnum:=tlong; END; { Randnum } { ********************************************* } { Get a random number with a specified ceiling. } FUNCTION GetRandWithCeiling(ceiling: LongInt): LongInt; BEGIN IF ceiling=0 THEN BEGIN GetRandWithCeiling:=0; Exit; END; GetRandWithCeiling:=Randnum(0) MOD (ceiling+1); END; { GetRandWithCeiling } {******************************************** ** Delay a passed number of seconds. **} PROCEDURE DelaySeconds( cnt: Integer ); VAR StartTick: LongInt; ThisTick: LongInt; Delay: LongInt; BEGIN StartTick := GetTickCount; ThisTick := GetTickCount; Delay := cnt * 1000; { ticks are in milliseconds} while( Delay > (ThisTick - StartTick) ) do begin ThisTick := GetTickCount; END; END; (********************************************** ** Get the system and ANSI fixed font's height. ** A bunch of routines need this for display. **) PROCEDURE GetFontHeight; VAR BWHDC: Hdc; { Device context } fmetrics: tTextMetric; { Grab text metrics } BEGIN BWHDC:=GetDC(HByteWin); SelectObject(BWHDC,GetStockObject(ANSI_Fixed_Font)); GetTextMetrics(BWHDC,fmetrics); ansiffontheight:=fmetrics.tmHeight; SelectObject(BWHDC,GetStockObject(System_Font)); GetTextMetrics(BWHDC,fmetrics); sysfontheight:=fmetrics.tmHeight; ReleaseDC(HByteWin,BWHDC); END; { GetSysFontHeight } (* ** Erase the client area *) PROCEDURE EraseClient; VAR xmin, xmax: Integer; { X clipping bounds } ymin, ymax: Integer; { Y clipping bounds } HisRect: TRect; { Client rectangle } HDevCont: HDC; { Device context } BEGIN { Get a device context for the window } HDevCont:=GetDC(HByteWin); { Black brush, null pen } SelectObject(HdevCont,GetStockObject(Black_Brush)); SelectObject(HdevCont,GetStockObject(Black_Pen)); { Get the rectangle coordinates } GetClientRect(HByteWin,HisRect); xmin:=HisRect.left; ymin:=HisRect.top; xmax:=HisRect.right; ymax:=HisRect.bottom; { Paint the rectangle } Rectangle(HdevCont,xmin,ymin,xmax,ymax); { Release the context } ReleaseDC(HBytewin, HDevCont); END; {******************************************** ** Initialize the display variables. **} PROCEDURE InitBenchDisplay; var fmetrics: TTextMetric; { For grabbing font metrics } begin BenchLineHDC := GetDC( HByteWin ); BenchLineX := 10; { Set us up with the system font } SelectObject( BenchLineHDC, GetStockObject(System_Font) ); { Determine starting coordinates } GetTextMetrics( BenchLineHDC, fmetrics ); { BenchLineY := fmetrics.tmHeight; BenchLineYInc := BenchLineY; } BenchLineY := sysfontheight; BenchLineYInc:= sysfontheight; { Clear the client window } EraseClient; { Set foreground and background colors } SetTextColor( BenchLineHDC, RGB(255,255,255) ); SetBkColor( BenchLineHDC, RGB(0,0,0) ); SetBkMode( BenchLineHDC, Opaque ); end; PROCEDURE CloseBenchDisplay; begin { Release the display context } ReleaseDC(HByteWin, BenchLineHDC ); end; (******************************************** ** Display a single line of benchmark results ********************************************* ** Pass this function the string to be displayed and the ** initial coordinates. It returns the height of the ** string, which can be used to position the next string ** in sequence. ** Note, I'm using globals all over the place which is rather messy. ** Still, its a lot cleaner than displaying each time. ** Definitely needs a rethink. *) FUNCTION DisplayBenchLine( hisstring: PChar; hisstringlen: Integer): Integer; VAR tlong: Longint; { Temp long integer } BEGIN if( BenchLineHDC <> 0 ) { ensure we've setup an HDC } then begin TextOut(BenchLineHDC, BenchLineX, BenchLineY, hisstring, hisstringlen); tlong := GetTextExtent(BenchLineHDC, hisstring, hisstringlen ); DisplayBenchLine := WordRec(tlong).hi; BenchLineY := BenchLineY + WordRec(tlong).hi; end; END; { DisplayBenchLine } { *************************************** } { WINDOWS INITIALIZATION ROUTINES } { *************************************** } PROCEDURE setParm( parmString: String ); BEGIN if( '-L' = parmString ) then begin autoLogAndExit := TRUE; end; if( not (Pos( '-', parmString ) = 1) ) then begin { not a flag -- must be the name of the log file. { note: we're not checking for validity here. but we do ensure proper length.} logFileName := Copy( parmString, 1, 12 ); end; END; {setParm } { *********************************** } { Initialize any parameters from the command line. } PROCEDURE InitCommandLineParameters; VAR i: Integer; { Loop index } lastParm : Integer; { number of parameters on command line } parmString : String; { a particular command line parameters } BEGIN lastParm := Paramcount; if( lastParm > 0 ) THEN BEGIN for i:= 1 to lastParm do BEGIN parmString := ParamStr( i ); setParm( parmString ); END; END; END; { *********************************** } { Initialize all the global variables } PROCEDURE InitGlobals; VAR i: Integer; { Loop index } fpath: ARRAY[0..fsPathName] of CHAR; { File path } BEGIN { Set times to their defaults } pixel_secs:=min_pixel_secs; line_secs:=min_line_secs; rect_secs:=min_rect_secs; poly_secs:=min_poly_secs; arce_secs:=min_arce_secs; bitblt_secs:=min_bitblt_secs; text_secs:=min_text_secs; lmem_secs := min_lmem_secs; gmem_secs := min_gmem_secs; rfile_io_secs := min_rfile_io_secs; {current interface does not use this, code does} sfile_io_secs := min_sfile_io_secs; {current interface/code does not use this } { Set other defaults } sfilesize:=5000000; { Seq. file is 5 million characters } sfilereclen[0]:=256; { Seq. file record lengths } sfilereclen[1]:=512; sfilereclen[2]:=1024; sfilereclen[3]:=4096; { Check all the checkboxes } FOR i:=1 TO max_test_id DO do_test_flags[i]:=TRUE; { Initialize the random number generator } Randnum(13); { Initialize the log file. } LogFileName := DefLogName; autoLogAndExit := FALSE; InitCommandLineParameters; someTests := FALSE; { no tests have yet been run } { Search for the comparison data file. Set appropriate flag depending on whether or the file is present or not. } StrPCopy(CompFileName,DefCompName); FileSearch(fpath,CompFileName,''); IF fpath[0]=#0 THEN iscompfilethere:=FALSE ELSE BEGIN iscompfilethere:=TRUE; Assign(DataFile,CompFileName); END; { Get the height of the system font } GetFontHeight; { Initialize the benchdisplayline variables. } benchLineX := 0; benchLineY := 0; benchLineYInc:= 0; benchLineHDC := 0; END; { InitGlobals } { ********************************** } { ** DIALOG BOX HANDLING ROUTINES ** } { ********************************** } (* **************************************** ** Given a button id, return the test id. *) FUNCTION ButtonToTestID(buttonid: Word) : Word; VAR i: Word; { Index variable } BEGIN ButtonToTestID:=0; (* Step through the button_ids[] array looking ** for a match. Exit with index value when ** you find one. *) FOR i:=1 TO max_test_id DO IF button_ids[i]=buttonid THEN BEGIN ButtonToTestID:=i; Exit; END; END; { ButtonToTestID } (* ******************************************* ** Save benchmark configuration settings in ** temporary storage. Call this routine ** when the configuration dialog box is first ** opened. This lets the system restore the ** initial setting if a CANCEL is hit. *) PROCEDURE SaveConfigToTemp; VAR i: Integer; { Index variable } BEGIN FOR i:=1 TO max_test_id DO tdo_test_flags[i]:=do_test_flags[i]; END; { SaveConfigToTemp } (* ******************************************** ** Restore benchmark configuration settings ** from temporary storage. Call this routine ** if a CANCEL button is hit. *) PROCEDURE RestoreConfigFromTemp; VAR i: Integer; { Integer variable } BEGIN FOR i:=1 TO max_test_id DO do_test_flags[i]:=tdo_test_flags[i]; END; { RestoreConfigFromTemp } { ************************************************ } { Verify that defaults selected are within bounds. } { This function examines the selected defaults and } { returns 0 if they're all ok. Otherwise, it } { returns the ID+1 of the first editbox that holds } { disallowed values. } FUNCTION CheckInBounds(HDialog : HWnd): Word; VAR i: Word; { Used as index } secs: Integer; { Seconds } transflag: Bool; { Translation flag } BEGIN FOR i:= 0 TO 7 DO BEGIN secs:=GetDlgItemInt(HDialog,edit_box_ids[i], @transflag, TRUE); IF (transflag=FALSE) OR (secs0 THEN SetDlgItemText(Dialog, IDL_Logfield, AlertDlgText); { Set the focus } SetFocus(GetDlgItem(Dialog,IDB_OK)); StandardAlertDialog:=FALSE; EXIT; END; wm_Paint: BEGIN IF ADTCount<0 THEN { A negative ADTCount is a sneaky way of telling us to display the BYTE Logo. } BEGIN DlgDC:=GetDC(Dialog); bithand:=LoadBitMap(HInstance,'BYTELOGO'); HMDevCont:=CreateCompatibleDC(DlgDC); SelectObject(HMDevCont,bithand); BitBlt(DlgDC,125,10,200,68,HMDevCont,0,0,SrcCopy); DeleteDC(HMDevCont); ReleaseDC(Dialog,DlgDC); DeleteObject(bithand); END; StandardAlertDialog:=FALSE; END; wm_Command: CASE WParam OF IDB_OK: BEGIN EndDialog(Dialog,0); StandardAlertDialog:=TRUE; ADTCount:=0; { Don't need string } EXIT; END; END; { CASE WParam } END; { CASE Message } StandardAlertDialog:=FALSE; END; { StandardAlertDialog } FUNCTION getLogNameDialogAction( Dialog: HWnd; Message, WParam: Word; LParam: LongInt ): Bool; export; VAR DlgDC: HDC; { Context for dialog } theName: ARRAY [0..14] of CHAR; strName: String[13]; BEGIN CASE Message OF wm_InitDialog: BEGIN StrPCopy( theName, logfileName ); SetDlgItemText(Dialog, IDL_Logfield, theName ); { Set the focus } SetFocus(GetDlgItem(Dialog,IDL_logfield)); getLogNameDialogAction:=FALSE; EXIT; END; wm_Command: CASE WParam OF IDL_OK: {grab the new logfile -- eventually do error checking } BEGIN EndDialog(Dialog,0); getLogNameDialogAction:=TRUE; GetDlgItemText( Dialog, IDL_LogField, theName, 13); strName := StrPas( theName ); { do error checking here -- if ok, then ...} logFileName := strName; EXIT; END; IDL_CANCEL: {don't change the logfile name} BEGIN EndDialog( Dialog, 0 ); getLogNameDialogAction := TRUE; EXIT; END; END; { CASE WParam } END; { CASE Message } getLogNameDialogAction:=FALSE; END; { ************************************* } { Handle the set-to-defaults dialog box } { This dialog box simply accepts a yes/no response. } { Yes sets benchmark configurations to their } { defaults...no leaves them unchanged. } FUNCTION SetToDefaultsDialog(Dialog: HWnd; Message, WParam: Word; LParam: LongInt): Bool; export; BEGIN CASE Message OF wm_InitDialog: BEGIN { Set the focus } SetFocus(GetDlgItem(Dialog,IDB_OK)); SetToDefaultsDialog:=FALSE; EXIT; END; wm_Command: BEGIN CASE WParam OF IDB_OK: BEGIN { Set all items to default } EndDialog(Dialog,0); SetToDefaultsDialog:=TRUE; EXIT; END; IDB_CANCEL: BEGIN EndDialog(Dialog,0); SetToDefaultsDialog:=TRUE; EXIT; END; END; { CASE WParam } END; { BEGIN } END; { CASE Message } SetToDefaultsDialog:=FALSE; END; { SetToDefaultsDialog } (* ********************** ** Flip a dialog button ** This routine flips the state of a dialog button. *) FUNCTION FlipButton(Dialog: HWnd; button_id: Integer; curr_state : Bool) : Bool; BEGIN IF curr_state=TRUE THEN BEGIN CheckDlgButton(Dialog,button_id,0); FlipButton:=FALSE; END ELSE BEGIN CheckDlgButton(Dialog,button_id,1); FlipButton:=TRUE; END; END; { FlipButton } { ********************************************* } { Handle the benchmark configuration dialog box } { This is the big, nasty dialog box that gets } { benchmark configuration settings. We grab } { stuff into a temporary area when we get an } { init dialog message. If the use hits an ok, } { we copy the temporary area into the real } { variables...if the user hits cancel, we dump } { the temporary stuff and keep the real } { variables unchanged. } FUNCTION BenchConfigDialog(Dialog: HWnd; Message, WParam: Word; LParam: LongInt): Bool; export; VAR test_id: Word; { Test Identification number } i: Integer; { Index value } BEGIN BenchConfigDialog:=True; CASE Message OF wm_InitDialog: { ** INITIALIZE DIALOG BOX ** } BEGIN { Copy current into temporary variables } SaveConfigToTemp; { Set buttons based on current values } FOR i:=1 TO max_test_id DO IF do_test_flags[i] THEN CheckDlgButton(Dialog,button_ids[i],1) ELSE CheckDlgButton(Dialog,button_ids[i],0); { Set edit boxes based on current values } SetDlgItemInt(Dialog,ID_PIXEL_SECS,pixel_secs, TRUE); SetDlgItemInt(Dialog,ID_LINE_SECS,line_secs, TRUE); SetDlgItemInt(Dialog,ID_RECT_SECS,rect_secs, TRUE); SetDlgItemInt(Dialog,ID_POLY_SECS,poly_secs, TRUE); SetDlgItemInt(Dialog,ID_ARCE_SECS,arce_secs, TRUE); SetDlgItemInt(Dialog,ID_BITBLT_SECS,bitblt_secs, TRUE); SetDlgItemInt(Dialog,ID_TEXT_SECS,text_secs,TRUE); { Set the focus } SetFocus(GetDlgItem(Dialog,IDB_OK)); BenchConfigDialog:=FALSE; EXIT; END; wm_Command: { ** HANDLE COMMAND ACTION ** } CASE WParam OF IDB_OK: { ** OK Button ** } BEGIN (* Make sure all the settings make sense and ** are within bounds. If not, throw up an ** alert dialog and keep things as they ** are. If so, read the benchmark configurations ** to make them current and get rid of the ** dialog box. *) IF CheckInBounds(Dialog)<> 0 THEN BEGIN (* Throw up alert *) MessageBeep(0); END ELSE BEGIN (* All is well *) GetBenchConfigSettings(Dialog); END; EndDialog(Dialog,NULL); BenchConfigDialog:=TRUE; EXIT; END; IDB_CANCEL: { ** Cancel button ** } BEGIN (* Set everything back to the way it ** was before the dialog box was ** opened. Then shut down. *) RestoreConfigFromTemp; EndDialog(Dialog,NULL); BenchConfigDialog:=TRUE; EXIT; END; ID_PIXEL_BUTTON, { ** Pixel test button ** } ID_LINES_BUTTON, { ** Lines test button ** } ID_RECTS_BUTTON, { ** Rectangles test button ** } ID_POLYS_BUTTON, { ** Polygons test button ** } ID_ARCE_BUTTON, { ** Arc/ellipse test button ** } ID_BITBLT_BUTTON, { ** BitBlt test button ** } ID_LMEM_BUTTON, { ** Local memory test button ** } ID_GMEM_BUTTON, { ** Global memory test button ** } ID_SFIO_BUTTON, { ** Seq. file i/o test button ** } ID_RFIO_BUTTON, { ** Rand. file i/o test button ** } ID_TEXT_BUTTON: { ** Text test button ** } BEGIN test_id:=ButtonToTestID(WParam); do_test_flags[test_id]:=FlipButton(Dialog,WParam, do_test_flags[test_id]); BenchConfigDialog:=TRUE; EXIT; END; ELSE BEGIN BenchConfigDialog:=FALSE; EXIT; END; END; { CASE WParam } ELSE BEGIN BenchConfigDialog:=FALSE; EXIT; END; END; { CASE Message } END; { BenchConfigDialog } (********************************** ** DEVICE CONFIGURATION ROUTINES ** **********************************) (**************************************** ** Show the current system configuration. ** This routine displays stuff like ** processor/coprocessor type, screen size, etc. *) PROCEDURE ShowDevConfig; BEGIN { Call assembly routine to determine processor and } { coprocessor type/presence. } { Call GetDeviceCaps for screen info. } { Clear the display area } { Display the information and return } END; { ShowDevConfig } { ************************************ } { ** BENCHMARK ROUTINES THEMSELVES! ** } { ************************************ } FUNCTION LMin(a,b: Integer): Longint; BEGIN IF amax_local_memsize); { Set up the flags word for upcoming requests } memflags1:=lmem_Moveable OR lmem_ZeroInit; memflags2:=lmem_Moveable; locsize:=0; { Enter timing loop } WHILE (elapsedtsecarray[lmem_test_id] DIV 1000)0 THEN memhandles[i+i+1]:=temphand; LocalFree(memhandles[i+i+1]); locsize:=locsize+reqsizes[i+i+1]; END; { Stop timing and record results } StopStopWatch; { Increment iterations and accumulate seconds } Inc(iterarray[lmem_test_id]); AccumTiming(lmem_test_id); END; { While } END; { LmemTest } (********************* ** Global memory test ** This procedure executes the global memory test. *) PROCEDURE GMemTest; VAR i: Integer; { Array index } numreqs: Word; { # of requests } memhandles: ARRAY[0..1000] OF THandle; { Memory handles } reqsizes: ARRAY[0..1000] OF LongInt; { Request sizes } totrequest: LongInt; { Total request } memflags1: Word; { Flags for requests } memflags2: Word; { Flags for requests } tmemptr: Pointer; { Temp. for holding pointer } globsize: LongInt; { Size of largest global block } dlgproc: TFarProc; { Procedure instance } reqsize: LongInt; { Max request size } tstring: Array[0..8] of Char; { Temp string for conversion } temphand: THandle; { Temporary handle } outstr: Array [0..80] of Char; { message to display } outstrlen: Integer; { need to know how long it is } BEGIN InitBenchDisplay; strPCopy( @outstr, 'Running global memory test.'); outstrlen := StrLen( @outstr ); DisplayBenchLine( @outstr, outstrlen ); { Clear timing } elapsedtsecarray[gmem_test_id]:=0; iterarray[gmem_test_id]:=0; { Determine amount of Global memory with call... } { ... to GlobalCompact(). If this is less than } { 1024K, then we've got a problem. } globsize:=GlobalCompact(1050000); globsize:=GetFreeSpace(0); IF globsize<1048576 THEN BEGIN (* Handle problem of < 1M of global memory. This should ** throw up a dialog box that says "Not enough memory for ** global test." With just an OK. After the user clicks ** the OK, the dialog box goes away and this function ** exits. *) ADTx:=28; { Display current memory in dialog } ADTy:=30; Str(globsize DIV 1024,tstring); StrCopy(@AlertDlgText,'('); StrCat(@AlertDlgText,@tstring); StrCat(@AlertDlgText,'K Free)'); ADTCount:=StrLen(@AlertDlgText); dlgproc:=MakeProcInstance(@StandardAlertDialog,HInstance); DialogBox(HInstance,'NOMEMDLG',HByteWin,dlgproc); FreeProcInstance(dlgproc); Exit; END; { Generate a random array of memory requests. } { Each request is no less than 512 bytes and } { no greater than 4K bytes. Furthermore, } { the sum of all requests will not exceed } { a specified amount. } numreqs:=0; totrequest:=0; REPEAT BEGIN reqsize:=ABS(GetRandWithCeiling(3584))+512; reqsizes[numreqs]:=reqsize; Inc(numreqs); totrequest:=totrequest+reqsize; END UNTIL (numreqs=max_global_memitems) OR (totrequest>max_global_memsize); { Set up the flags words for upcoming requests } memflags1:=gmem_Moveable OR gmem_ZeroInit; memflags2:=gmem_Moveable; globsize:=0; { Enter timing loop } WHILE (elapsedtsecarray[gmem_test_id] DIV 1000)0 THEN MessageBeep(0); END; { Go through the array of remaining blocks, issuing } { a GlobalReAlloc on those blocks, expanding each } { so that it consumes remaining memory. Then free } { that block and go on to the next. } FOR i:=0 TO ((numreqs-2) DIV 2) DO BEGIN temphand:= GlobalRealloc(memhandles[i+i+1],reqsizes[i+i+1]+globsize,memflags2); IF temphand<>0 THEN memhandles[i+i+1]:=temphand; memhandles[i+i+1]:=GlobalFree(memhandles[i+i+1]); IF memhandles[i+i+1]<>0 THEN MessageBeep(0); globsize:=globsize+reqsizes[i+i+1]; END; { Stop timing and record results } StopStopWatch; { Increment iterations and accumulate seconds } Inc(iterarray[gmem_test_id]); AccumTiming(gmem_test_id); END; { While } END; { GMemTest } { *************************** } { GRAPHICS BENCHMARK ROUTINES } { *************************** } { **************** } { Pixels benchmark } PROCEDURE PixelTest; VAR xmin, xmax: Integer; { X clipping bounds } ymin, ymax: Integer; { Y clipping bounds } xloc: ARRAY [0..pixels_per_iter] OF Integer; { X coordinates array } yloc: ARRAY [0..pixels_per_iter] OF Integer; { Y coordinates array } colors: ARRAY [0..pixels_per_iter] OF LongInt; { Colors array } i: Word; { Loop index } tlongint: LongInt; { Temp long integer } redval: Byte; { Holds RED value } greenval: Byte; { Holds GREEN value } blueval: Byte; { Holds BLUE value } HDevCont: HDC; { Device context } HisRect: TRect; { Client rectangle } menuhand: HMenu; { Handle for menu } omenuhand: HMenu; { Old menu handle } BEGIN { Set up the proper menu } menuhand:=LoadMenu(HInstance,'PIXMENU'); omenuhand:=GetMenu(HByteWin); SetMenu(HByteWin,menuhand); { Determine the clipping bounds for this test. } GetClientRect(HByteWin,HisRect); xmin:=HisRect.left; ymin:=HisRect.top; xmax:=HisRect.right; ymax:=HisRect.bottom; { Clear the timing arrays } elapsedtsecarray[pixel_test_id]:=0; iterarray[pixel_test_id]:=0; { Build an array of color references } FOR i:=0 TO pixels_per_iter DO BEGIN tlongint:=Randnum(0); redval:=ByteRec(WordRec(tlongint).lo).lo; tlongint:=Randnum(0); greenval:=ByteRec(WordRec(tlongint).lo).lo; tlongint:=Randnum(0); blueval:=ByteRec(WordRec(tlongint).lo).lo; colors[i]:=PaletteRGB(redval,greenval,blueval); END; { Get a device context } HDevCont:=GetDC(HByteWin); { ** SetPixel ** } { Enter timing loop } WHILE (elapsedtsecarray[pixel_test_id] DIV 1000) 32000 ) then begin nb := 32000; end else begin nb := flen; end; nb := Abs( GetRandWithCeiling( nb ) ); { AHA! the actual test! } StartStopWatch; ll_seekread( fh, offset, nb ); StopStopWatch; { accumulate the timing information } AccumTiming( rfio_test_id ); avg := avg + nb; rbytes := rbytes + nb; { close the file -- we're done } _lclose( fh ); end; {for loop} n := Abs( GetRandWithCeiling( 19 ) ); n := Abs( GetRandWithCeiling( 19 ) ); gfName( n, str ); fh := _lopen( str, of_Write ); flen := fileSize( fh ); offset := Abs( GetRandWithCeiling( flen ) ); nb := Trunc(avg / 3); if( offset + nb > flen ) then begin myMax := myMax + offset + nb - flen; if( myMax > MaxRandomIOBytes ) then begin exit end; end; StartStopWatch; ll_seekwrite( fh, offset, nb ); StopStopWatch; AccumTiming( rfio_test_id ); wbytes := wbytes + nb; _lclose( fh ); Inc(count); until( elapsedtsecarray[ rfio_test_id ] > doTime ); totalRandomIOBytes := rbytes + wbytes; { the tests are over, clean up the mess we've left } StrPCopy( @outstr, 'Deleting Files.'); outstrlen := StrLen( @outstr ); DisplayBenchLine( @outstr, outstrlen ); { zap the files } DeleteAllFiles; {zap the memory } Dispose( BigFileBuff ); END; {*************************** ** Add logfile header to output ** ** If toFile is true -- just send the data the log file. ** If tofile is false -- just send the data to the screen. ** n.b. We never send the information to both places. *} PROCEDURE logfileHeader( toFile: Boolean ); VAR outstr: Array [0..81] of Char; tempstr: Array [0..81] of Char; theHDC: HDC; outlen: Integer; tempWord: Word; tempInt: Integer; tempLInt: LongInt; tempLInt2:LongInt; year : Word; month : Word; day : Word; doweek: Word; hour : Word; minute: Word; second: Word; sec100: Word; BEGIN StrCopy( outstr, 'File: ' ); StrPCopy( tempstr, logFileName ); StrCat( outstr, tempstr ); if toFile then begin Writeln( logFile, outstr ); end else begin outLen := StrLen( outstr ); DisplayBenchLine( outstr, outLen ); end; GetDate( year, month, day, doweek ); StrCopy( outstr, 'Test run on: ' ); Str( month, tempstr ); StrCat( tempstr, '/' ); StrCat( outstr, tempstr ); Str( day, tempstr ); StrCat( tempstr, '/' ); StrCat( outstr, tempstr ); Str( year, tempstr ); StrCat( outstr, tempstr ); StrCat( outstr, ' at: ' ); GetTime( hour, minute, second, sec100 ); Str( hour, tempstr ); StrCat( outstr, tempstr ); StrCat( outstr, ':' ); if( minute < 10 ) then begin StrCat( outstr, '0' ); end; Str( minute, tempstr ); StrCat( outstr, tempstr ); if toFile then begin Writeln( logFile, outstr ); end else begin outLen := StrLen( outstr ); DisplayBenchLine( outstr, outLen ); end; tempWord := GetVersion; StrCopy( outstr, 'Windows Version: ' ); Str( Lo(tempWord), tempstr ); StrCat( outstr, tempstr ); StrCat( outstr, '.' ); Str( Hi(tempWord), tempstr); StrCat( outstr, tempstr ); if toFile then begin Writeln( logFile, outstr ); end else begin outLen := StrLen( outstr ); DisplayBenchLine( outstr, outLen ); end; tempInt := GetSystemMetrics( sm_Debug ); StrCopy( outstr, 'Debug version of Windows is ' ); if( 0 = tempInt ) then StrCat( outstr, 'not ' ); StrCat( outstr, 'installed.' ); if toFile then begin Writeln( logFile, outstr ); end else begin outLen := StrLen( outstr ); DisplayBenchLine( outstr, outLen ); end; StrCopy( outstr, 'Screen width is: ' ); tempInt := GetSystemMetrics( sm_CXScreen ); Str( tempInt, tempStr ); StrCat( outstr, tempStr ); StrCat( outstr, ' pixels wide by ' ); tempInt := GetSystemMetrics( sm_CYScreen ); Str( tempInt, tempStr ); StrCat( outstr, tempStr ); StrCat( outstr, ' pixels high.' ); if toFile then begin Writeln( logFile, outstr ); end else begin outLen := StrLen( outstr ); DisplayBenchLine( outstr, outLen ); end; tempLInt := GlobalCompact( 0 ); tempLInt := templint div 1024; Str( tempLInt, outstr ); StrCat( outstr, ' Kbytes free in global heap.' ); if toFile then begin Writeln( logFile, outstr ); end else begin outLen := StrLen( outstr ); DisplayBenchLine( outstr, outLen ); end; { theHDC := GetDC( HByteWin ); tempInt := GetDeviceCaps( theHDC, BitsPixel ); StrCopy( outstr, 'Bit depth is ' ); Str( tempInt, tempstr ); StrCat( outstr, tempstr ); StrCat( outstr, ' bits per pixel.' ); if toFile then begin Writeln( logFile, outstr ); end else begin outLen := StrLen( outstr ); DisplayBenchLine( outstr, outLen ); end; why is bit depth 1 pixel? } END; (*************************** ** Display benchmark results ** ** doLog indicates whether the information should be written to the named file. ** oldResults indicates whether we show currently selected tests, or all tests ** with results. ** n.b. test results are not cleared between passes. ****************************) PROCEDURE DisplayResults( doLog : Boolean; oldResults : Boolean ); VAR i: Integer; { Loop index } outstr: Array [0..80] of Char; { Output string } outstrlen: Integer; { Output string length } valstr: Array [0..20] of Char; { For holding values } numer: Real; { Used in calculations } denom: Real; { Same as above } result: Real; { Results figure } dtemp: Real; { For temporary results } BEGIN if( doLog ) { do we want to output to logfile? } then begin Assign( logFile, logFileName ); Rewrite( logfile ); logfileHeader(TRUE); end; InitBenchDisplay; StrCopy( outstr, ' TEST RESULTS' ); outstrlen := StrLen( outstr ); DisplayBenchLine( outstr, outstrlen ); { Examine the do_test_flags[] array to determine which tests were executed. Produce a line of text for each one. } FOR i:=1 TO max_num_tests DO BEGIN StrPCopy( @outstr, '' ); IF ( TRUE = do_test_flags[i] ) OR (TRUE = oldResults) THEN BEGIN { only process tests for which time has elapsed. takes care of showing old test results which have not actually been run! } if( elapsedtsecarray[i] > 0 ) then begin { Begin building the output string. } StrPCopy(@outstr,test_name[i]); numer:=iterarray[i]; denom:=elapsedtsecarray[i]; result := 0; {consider the case where denom is 0} if( denom > 0 ) then begin result:=( numer/denom) * 1000; end; CASE i OF pixel_test_id: BEGIN dtemp:=pixels_per_iter; result:=result * dtemp; { Pixels per second } Str(result:10:2,valstr); StrCat(@outstr,@valstr); StrCat(@outstr,' pix per sec') END; line_test_id: BEGIN dtemp:=lines_per_iter; result:=result * dtemp; { Lines per second } Str(result:10:2,valstr); StrCat(@outstr,@valstr); StrCat(@outstr,' lines per sec'); END; rect_test_id: BEGIN dtemp:=rects_per_iter; result:=result * dtemp; { Rectangles per second } Str(result:10:2,valstr); StrCat(@outstr,@valstr); StrCat(@outstr,' rects per sec'); END; poly_test_id: BEGIN dtemp:=polys_per_iter; result:=result * dtemp; { Polygons per second } Str(result:10:2,valstr); StrCat(@outstr,@valstr); StrCat(@outstr,' polys per sec'); END; arce_test_id: BEGIN dtemp:=ellps_per_iter; result:=result * dtemp; { Ellipses per second } Str(result:10:2,valstr); StrCat(@outstr,@valstr); StrCat(@outstr,' ellps per sec'); END; bitblt_test_id: BEGIN dtemp:=bmap_points; result:=result * dtemp; { Bitblt ops per second } Str(result:10:2,valstr); StrCat(@outstr,@valstr); StrCat(@outstr,' bitblts per sec'); END; lmem_test_id: BEGIN dtemp := iterarray[lmem_test_id]; result := dtemp / elapsedtsecarray[lmem_test_id]; {ms} result := result * 1000; {sec} Str( result:10:2, valstr ); StrCat( @outstr, @valstr ); StrCat( @outstr, ' iterations per sec' ); END; gmem_test_id: BEGIN dtemp := iterarray[gmem_test_id]; result := dtemp / elapsedtsecarray[gmem_test_id]; {ms} result := result * 1000; {sec} Str( result:10:2, valstr ); StrCat( @outstr, @valstr ); StrCat( @outstr, ' iterations per sec' ); END; sfio_test_id: BEGIN dtemp := (sfio_bytes_per_iter * numer) / 1024; {bytes -> Kbytes} result := dtemp * result; {Kbytes / second} Str( result:10:2, valstr ); StrCat( @outstr, @valstr ); StrCat( @outstr, ' Kbytes per sec' ); END; rfio_test_id: BEGIN dtemp := (totalRandomIoBytes * result) / 1024; Str( dtemp:10:2, valstr ); StrCat( @outstr, @valstr ); StrCat( @outstr, ' Kbytes per sec' ); END; texto_test_id: BEGIN dtemp:=text_per_iter; result:=result * dtemp; { Textout lines per second } Str(result:10:2,valstr); StrCat(@outstr,@valstr); StrCat(@outstr,' TextOut ips '); END; dtext_test_id: BEGIN dtemp:=text_per_iter; result:=result * dtemp; { Drawtext lines per second } Str(result:10:2,valstr); StrCat(@outstr,@valstr); StrCat(@outstr,' Drawtext ips'); END; END; { CASE } { Display one line of results } outstrlen:=StrLen(@outstr); if( outstrlen > 0 ) then begin DisplayBenchLine(@outstr, outstrlen ); { See if we also need to send to output file. } if( doLog ) then begin Writeln( logFile, outstr ); end; end; {string has content } END; { if time has elapsed } END; END; { FOR } CloseBenchDisplay; { close file if we were writing to one. } if( doLog ) then begin Close( logFile ); end; END; { DisplayResults } (*************************************************** ** Right justify a string. This has the effect of ** padding the string on the left with blanks until ** the string fills out the field width. *) PROCEDURE RightJustifyString( fieldwidth : Integer; strng : PChar); VAR tempstr: ARRAY[0..60] of Char; BEGIN { Add fieldwidth-length(strng) bytes to our temp string. Note that if strng is already longer than fieldwidth, we gotta truncate. } tempstr[0]:=#0; IF fieldwidth-StrLen(strng) > 0 THEN BEGIN FillChar(tempstr[0],60,#0); FillChar(tempstr[0],fieldwidth-StrLen(strng),' '); END; { Now concatenate str to the end of tempstr, and move the results back into strng so it can go home. } StrCat(tempstr,strng); StrCopy(strng,tempstr); END; { RightJustifyString } (************************************************************ ** Fill out a string to n bytes. Pads on right with blanks. ** This routine will also truncate if necessary. *) PROCEDURE FillOutString( fieldwidth : Integer; strng: PChar ) ; BEGIN { Attach fieldwidth-Length(string) blanks. Don't bother if that's a negative number or zero. } IF fieldwidth-StrLen(strng) > 0 THEN FillChar(strng[StrLen(strng)],fieldwidth-StrLen(strng),' '); strng[fieldwidth-1]:=#0; END; { FillOutstring } (************************************************* ** Display a line of test comparison information. ** This routine determines whether the current line is ** greater than the starting line, but less than the ** ending line. If so, the current line can be displayed. ** If not, then it simply updates the current line ** and returns without displaying anything. ** NOTE: This routine borrows the previously-defined ** routine DisplayBenchLine, which updates screen ** positions appropriately. *) PROCEDURE DispCompTestLine( testnum: Integer; VAR currentline: Integer ); VAR lineotext: ARRAY[0..59] of Char; { Line to output } tempstr: ARRAY[0..20] of Char; { A temp. string } result: Real; { Value from file } iterspersec: Real; { Iterations per second } i: Integer; { Loop index } BEGIN { Verify that the line we are about to display is in the active portion of the window. } IF (currentline>=bcBeginLine) AND (benchLineY'*' THEN BEGIN { Allocate some memory for this guy } fbhand[i]:=GlobalAlloc(GMEM_MOVEABLE OR GMEM_NODISCARD,SizeOf(CompData)); fbptr:=GlobalLock(fbhand[i]); StrCopy(fbptr^.description,systemname); { Read in system name } READLN(DataFile,systemname); StrCopy(fbptr^.sysname,systemname); { Read in the test results. } FOR j:= 1 TO max_test_id DO BEGIN READLN(DataFile,tempreal); fbptr^.testresult[j]:=tempreal; END; { Unlock this handle so the global heap can compact if it needs to. } GlobalUnlock(fbhand[i]); Inc(i); { Advance to next handle } END; UNTIL systemname[0]='*'; bcNumSystems:=i-1; { # of systems in the table } { Calculate the total # of lines in the display. Use this to set the range of the scrollbar. } bcTotNumLines:= 2+bcNumSystems * (4+max_num_tests); { Set scrollbar range and initialize it } SetScrollRange(Dialog,sb_VERT,1,bcTotNumLines,FALSE); SetScrollPos(Dialog,sb_VERT,1,TRUE); bcBeginLine:=1; { Set the focus } SetFocus(GetDlgItem(Dialog,IDB_OK)); { Initialize screen coordinate stuff to make the DisplayBenchLine routine happy. } BenchLineHDC := GetDC( Dialog ); BenchLineX := 10; { Set us up with a fixed pitch font } SelectObject( BenchLineHDC, GetStockObject(ANSI_Fixed_Font) ); BenchLineY := ansiffontheight; BenchLineYInc:= ansiffontheight; { Get old background color } oldwinbkcolor:=GetSysColor(color_Window); { Set foreground and background colors } syscoloridx[0]:=color_Window; syscolorval[0]:=RGB(255,255,255); SetSysColors(1,syscoloridx,syscolorval); SetTextColor( BenchLineHDC, RGB(0,0,0) ); SetBkColor(BenchLineHDC,RGB(255,255,255)); SetBkMode(BenchLineHDC,Opaque); END; { wm_InitDialog } wm_VScroll: BEGIN doInvalid:=FALSE; CASE WParam OF { NOTE: In the scrolling stuff, the variable bcBeginLine indicates which line is at the top of the display box. We calculate everything else based on bcBeginLine. } sb_Top: BEGIN bcBeginLine:=1; doInvalid:=TRUE; END; sb_Bottom: BEGIN bcBeginLine:=bcTotNumLines-1; doInvalid:=TRUE; END; sb_LineUp: BEGIN IF bcBeginLine>1 THEN bcBeginLine:=bcBeginLine-1; doInvalid:=TRUE; END; sb_LineDown: BEGIN IF bcBeginLine<(bcTotNumLines-1) THEN bcBeginLine:=bcBeginLine+1; doInvalid:=TRUE; END; sb_PageUp: BEGIN bcBeginLine:=bcBeginLine-5; IF bcBeginLine<1 THEN bcBeginLine:=1; doInvalid:=TRUE; END; sb_PageDown: BEGIN bcBeginLine:=bcBeginLine+5; IF bcBeginLine>(bcTotNumLines-1) THEN bcBeginLine:=bcTotNumLines-1; doInvalid:=TRUE; END; sb_ThumbPosition: BEGIN bcBeginLine:=WordRec(LParam).lo; doInvalid:=TRUE; END; END; { CASE WParam } SetScrollPos(Dialog,sb_VERT,bcBeginLine,TRUE); IF doInvalid THEN InvalidateRect(Dialog,@bcDispRect,TRUE); END; { CASE wm_Vscroll } wm_Paint: BEGIN { Clear the display area } HDlgDC:=BeginPaint(Dialog,DlgPntStruct); { Initialize the cursor position } currentline:=1; { Start a line counter } benchLineY:=sysfontheight; i:=1; { i counts # of systems } REPEAT { Lock the handle...resolving to pointer } fbptr:=GlobalLock(fbhand[i]); IF (currentline>=bcBeginLine) AND (benchLineY=bcBeginLine) AND (benchLineY=bcBeginLine) AND (benchLineYBenchLineHDCheight); EndPaint(Dialog,DlgPntStruct); END; { wm_Paint } wm_Command: BEGIN CASE WParam OF IDB_OK: { User pushed Exit button } BEGIN { Get rid of all the handles we allocated. } FOR i:=0 to (bcNumSystems-1) DO BEGIN GlobalUnlock(fbhand[i]); GlobalFree(fbhand[i]); END; ReleaseDC(Dialog,BenchLineHDC); EndDialog(Dialog,NULL); BenchCompDialog:=TRUE; syscolorval[0]:=oldwinbkcolor; syscoloridx[0]:=color_Window; SetSysColors(1,syscoloridx,syscolorval); Exit; END; { IDB_OK } END; { CASE WParam } END; { wm_Command } END; { CASE Message } BenchCompDialog:=FALSE; END; { BenchCompDialog } (****************************************************** ** DiskSpaceAlert ** This procedure throws up the alert that tells the ** guy he doesn't have enough disk space to run the ** disk space test. *) PROCEDURE DiskSpaceAlert; VAR dlgproc: TFarProc; { Procedure instance } BEGIN ShowCursor(TRUE); ADTx:=28; ADTy:=20; StrCopy(@AlertDlgText,'Disk tests require 6 MB of free disk space'); ADTCount:=StrLen(@AlertDlgText); dlgproc:=MakeProcInstance(@StandardalertDialog,HInstance); DialogBox(HInstance,'NOMEMDLG',HByteWin,dlgproc); FreeProcInstance(dlgproc); ShowCursor(FALSE); END; { DiskSpaceAlert } (********************************************************* ** Execute the benchmarks selected and record the results ** in the proper places. *) PROCEDURE DoTests; VAR i: Integer; { Loop index } BEGIN { Turn the cursor off } ShowCursor(FALSE); FOR i := 1 TO max_num_tests DO IF do_test_flags[i] THEN BEGIN CASE i OF pixel_test_id: BEGIN PixelTest; END; line_test_id: BEGIN LineTest; END; rect_test_id: BEGIN RectTest; END; poly_test_id: BEGIN PolygonTest; END; arce_test_id: BEGIN EllipseTest; END; bitblt_test_id: BEGIN BitbltTest; END; lmem_test_id: BEGIN LMemTest; END; gmem_test_id: BEGIN GMemTest; END; sfio_test_id: BEGIN { Make sure we have enough space } IF DiskFree(0)