(* **************************************************************************** * * "DHRYSTONE" Benchmark Program * ----------------------------- * * Version: Pascal, Version 2.1 * * File: dhry.p * * Date: May 25, 1988 * * Author: Reinhold P. Weicker * Siemens AG, E STE 35 * Postfach 3240 * 8520 Erlangen * Germany (West) * Phone: [xxx-49]-9131-7-20330 * (8-17 Central European Time) * Usenet: ..!mcvax!unido!estevax!weicker * * Original Version (in Ada) published in * "Communications of the ACM" vol. 27., no. 10 (Oct. 1984), * pp. 1013 - 1030, together with the statistics * on which the distribution of statements etc. is based, * * This version uses calls to the Pascal runtime library of the * Berkeley UNIX system (4.3 bsd) for time measurement. * For measurements on other systems, these calls need to be * modified. * * Collection of Results: * Reinhold Weicker (address see above) and * * Rick Richardson * PC Research. Inc. * 94 Apple Orchard Drive * Tinton Falls, NJ 07724 * Phone: (201) 834-1378 (9-17 EST) * Usenet: ...!seismo!uunet!pcrat!rick * * Please send results to Rick Richardson and/or Reinhold Weicker. * Complete information should be given on hardware and software used. * Hardware information includes: Machine type, CPU, type and size * of caches; for microprocessors: clock frequency, memory speed * (number of wait states). * Software information includes: Compiler (and runtime library) * manufacturer and version, compilation switches, OS version. * The Operating System version may give an indication about the * compiler; Dhrystone itself performs no OS calls in the measurement loop. * * The complete output generated by the program should be mailed * such that at least some checks for correctness can be made. * **************************************************************************** * * History: This version Pascal/2.1 has been made for two reasons: * * 1) There is a need for a common Pascal version of * Dhrystone. Although translation from the published (Ada) * version to Pascal is straightforward in most aspects, * there are cases where it may not be obvious to everyone. * There should be, as far as possible, only one Pascal version * of Dhrystone such that results can be compared without * restrictions. Also, a Pascal version of Dhrystone has not yet * found a network distribution comparable to the C version * (version 1.1) distributed by Rick Richardson. * * 2) As far as it is possible without changes to the Dhrystone * statistics, optimizing compilers should be prevented from * removing significant statements. * * This Pascal version 2.1 has been made consistent with the * C version 2.1; therefore the acknowledgments for the C version * are due for the Pascal version as well: I thank * Rick Richardson (Tinton Falls, NJ), Chaim Benedelac (Nat. * Semi.), David Ditzel (SUN), Earl Killian and John Mashey (MIPS), * Alan Smith and Rafael Saavedra-Barrera (UC at Berkeley) * for their help with comments on earlier versions of the * benchmark. * * Changes: In the initialization part, this version differs * from the Pascal version previously distributed by Reinhold * Weicker, the number of runs through Dhrystone is obtained * interactively from the terminal. Output of the result * has been changed to conform to the C version (2.1). * The changes in the initialization part and in the printing * of the result have no impact on performance measurement * since they are outside the measaurement loop. * * Inside the measurement loop, this version follows the * version previously distributed by Reinhold Weicker. * As a correction to the published version, a statement * initializing Array2Glob [8,7] (outside the measurement * loop) has been added. Otherwise, this array element would * have an undefined value. * * At several places in the benchmark, code has been added, * but within the measurement loop only in branches that * are not executed. The intention is that optimizing compilers * should be prevented from moving code out of the measurement * loop, or from removing code altogether. Since the statements * that are executed within the measurement loop have NOT been * changed, all numbers defining the "Dhrystone distribution" * (distribution of statements, operand types and locality) * still hold. Except for sophisticated optimizing compilers, * execution times for this version should be the same as * for previous versions. * * Since it has proven difficult to subtract the time for the * measurement loop overhead in a correct way, the loop check * has been made a part of the benchmark. This does have * an impact - though a very minor one - on the distribution * statistics which have been updated for this version. * * All changes within the measurement loop are described * and discussed in the companion paper "Rationale for * Dhrystone version 2". * * Because of the self-imposed limitation that the order and * distribution of the executed statements should not be * changed, there are still cases where optimizing compilers * may not generate code for some statements. To a certain * degree, this is unavoidable for small synthetic benchmarks. * Users of the benchmark are advised to check code listings * whether code is generated for all statements of Dhrystone. * * Version 2.1 is identical to version 2.0 distributed via * the UNIX network Usenet in March 1988 except that it corrects * some minor deficiencies that were found by users of version 2.0. * The only change within the measurement loop is that a * non-executed "else" part was added to the "if" statement in * Func3, and a non-executed "else" part removed from Proc3. * *************************************************************************** * * Compilation model and measurement (IMPORTANT): * * This program contains the Dhrystone program, including measurement setup, * in one file. The original (Ada) program contained three packages, * - a package with global type definitions, * - Pack_1, containing the main program (Proc_0 in Ada) and procedures * Proc_1, ... , Proc_5, * - Pack_2, containing Proc_6, ... , Proc_8, Func_1, ..., Func_3. * Since ISO/ANSI Standard Pascal provides no means to express separate * compilation (although many Pascal implementations provide such a feature), * it is not possible to formulate a portable Pascal version with the program * in several modules, corresponding more closely to the Ada and C versions. * Therefore, no attempt has been made to construct a Pascal version with * the program consisting of several modules. * * This difference may impact execution time because the compiler can * perform more data flow analysis for a single-module program; * sophisticated compilers may be able to suppress code generation for * some parts of the program. * Users should check machine code listings generated by the compiler * to ensure that code is generated for all parts of the program. * * The following "ground rules" apply for measurements: * - No procedure merging * - Otherwise, compiler optimizations are allowed but should be indicated * See the companion paper "Rationale for Dhrystone Version 2" for a more * detailed discussion of these ground rules. * * For 16-Bit processors (e.g. 80x86), times for all compilation models * ("small", "medium", "large") should be given if possible, together * with a definition of these models for the compiler system used. * ************************************************************************** * * Dhrystone (Pascal version) statistics: * * [Comment from the first distribution by Reinhold Weicker, * the distribution statistics have been updated for Version 2.1. * Note that because of language differences, the numbers are different * from the Ada version. The main difference is that the variables that * are local variables of "Proc_0" (Ada) or "main" (C) are global * variables in the Pascal version.] * * The following program contains statements of a high level programming * language (here: Pascal) in a distribution considered representative: * * assignments 58 * control statements 28 * procedure, function calls 15 * * 100 statements are dynamically executed. The program is balanced with * respect to the three aspects: * * - statement type * - operand type (for simple data types) * - operand access * operand global, local, parameter, or constant. * There is no static nesting of blocks or procedures, * therefore all variables are either global or local. * * The combination of these three aspects is balanced only approximately. * * 1. Statement Type: * ----------------- number * * V1 := V2 15 * V := Constant 12 * (incl. V1 := F(..) * Assignment, 7 * with array element * Assignment, 6 * with record component * -- * 40 40 * * X := Y +|-|and|or Z 5 * X := Y +|-|"=" Constant 6 * X := X +|- 1 3 * X := Y *|/ Z 2 * X := Expression, 1 * two operators * X := Expression, 1 * three operators * -- * 18 18 * * if .... then .... 14 * with "else" 7 * without "else" 7 * executed 3 * not executed 4 * for I in 1..N do ... 7 | counted every time * while ... do ... 4 | the loop condition * repeat ... until 1 | is evaluated * case ... end 1 * with 1 * -- * 28 28 * * P (...) procedure call 10 * X := F (...) * function call 5 * -- * 15 15 * --- * 101 * * 22 of the 58 assignments have a variable of a constrained * (sub-)type as their destination. In general, discriminant checks * will be necessary in these cases; however, the compiler may * optimize out some of these checks. * * The average number of parameters in procedure or function calls * is 1.80 (not counting the function values as implicit parameters). * * * 2. Operators * ------------ * number approximate * percentage * * Arithmetic 27 52.9 * * + 16 31.4 * - 7 13.7 * * 3 5.9 * div 1 2.0 * * Comparison 20 39.2 * * = 9 17.6 * <> 4 7.8 * > 1 2.0 * < 3 5.9 * >= 1 2.0 * <= 2 3.9 * * Logic 4 7.8 * * AND 1 2.0 * OR 1 2.0 * NOT 2 3.9 * * -- ----- * 51 99.9 * * * 3. Operand Type (counted once per operand reference): * --------------- * number approximate * percentage * * Integer 135 54.7 % * Character 47 19.0 % * Enumeration 31 12.6 % * Boolean 13 5.3 % * Pointer 11 4.5 % * String30 6 2.4 % * Array 2 0.8 % * Record 2 0.8 % * --- ------- * 247 100.1 % * * When there is an access path leading to the final operand (e.g. a record * component), only the final data type on the access path is counted. * * There are 16 accesses to components of a record, 9 of them go to * a component in a variant part. For some of these accesses, the * compiler may suppress generation of code checking the tag field * during optimization. * * * 3. Operand Locality: * ------------------- * * local variable 84 34.0 % * global variable 58 23.5 % * parameter 45 18.2 % * value 23 9.3 % * reference 22 8.9 % * function result 5 2.0 % * constant 55 22.3 % * --- ------- * 247 100.0 % * * * The program does not compute anything meaningful, but it is syntactically * and semantically correct. All variables have a value assigned to them * before they are used as a source operand. * * There may be cases where a highly optimizing compiler may recognize * unnecessary statements and may not generate code for them. * * There has been no explicit effort to account for the effects of a * cache, or to balance the use of long or short displacements for code or * data. * **************************************************************************** *) program Dhrystone (input, output); (***************) const (* for measurement *) MicrosecondsPerClock = 1000; ClocksPerSecond = 1000; (* In Berkeley UNIX Pascal, the function "clock" *) (* returns milliseconds *) TooSmallTime = 2000; (* Measurements should last at least 2 seconds *) type (* Global type definitions *) Enumeration = (Ident1, Ident2, Ident3, Ident4, Ident5); OneToThirty = 1..30; OneToFifty = 1..50; CapitalLetter = 'A'..'Z'; String30 = packed array [OneToThirty] of char; Array1DimInteger = array [OneToFifty] of integer; Array2DimInteger = array [OneToFifty, OneToFifty] of integer; RecordPointer = ^RecordType; RecordType = record PointerComp: RecordPointer; case Discr: Enumeration of Ident1: (* only this variant is used, *) (* but in some cases discriminant *) (* checks are necessary *) (EnumComp: Enumeration; IntComp: OneToFifty; StringComp: String30); Ident2: (Enum2Comp: Enumeration; String2Comp: String30); Ident3, Ident4, Ident5: (Char1Comp, Char2Comp: char); end; (* record *) var (* Ada version: Variables local in Proc_0 *) Int1Glob, Int2Glob, Int3Glob: OneToFifty; CharIndex: char; EnumGlob: Enumeration; String1Glob, String2Glob: String30; (* Ada version: Variables global in Pack_1 *) PointerGlob, NextPointerGlob: RecordPointer; IntGlob: integer; BoolGlob: boolean; Char1Glob, Char2Glob: char; Array1Glob: Array1DimInteger; Array2Glob: Array2DimInteger; (* Variables for measurement *) RunIndex, NumberOfRuns, BeginClock, EndClock, SumClocks: integer; Microseconds, DhrystonesPerSecond: real; I: integer; (* end of variables for measurement *) procedure Proc1 ( PointerParVal: RecordPointer); forward; procedure Proc2 (var IntParRef: OneToFifty); forward; procedure Proc3 (var PointerParRef: RecordPointer); forward; procedure Proc4; forward; (* without parameters *) procedure Proc5; forward; (* without parameters *) procedure Proc6 ( EnumParVal: Enumeration; var EnumParRef: Enumeration); forward; procedure Proc7 ( Int1ParVal, Int2ParVal: OneToFifty; var IntParRef: OneToFifty); forward; procedure Proc8 (var Array1ParRef: Array1DimInteger; var Array2ParRef: Array2DimInteger; Int1ParVal, Int2ParVal: integer); forward; function Func1 ( Char1ParVal, Char2ParVal: CapitalLetter): Enumeration; forward; function Func2 (var String1ParRef, String2ParRef: String30): boolean; forward; function Func3 ( EnumParVal: Enumeration): boolean; forward; procedure Proc1; (* (PointerParVal: RecordPointer) *) (* executed once *) begin with PointerParVal^.PointerComp^ (* = PointerGlobNext *) do begin PointerParVal^.PointerComp^ := PointerGlob^; PointerParVal^.IntComp := 5; IntComp := PointerParVal^.IntComp; PointerComp := PointerParVal^.PointerComp; Proc3 (PointerComp); (* PointerParVal^.PointerComp^.PointerComp = PointerGlob^.PointerComp *) if Discr = Ident1 then (* executed *) begin IntComp := 6; Proc6 (PointerParVal^.EnumComp, EnumComp); PointerComp := PointerGlob^.PointerComp; Proc7 (IntComp, 10, IntComp); end (* then *) else (* not executed *) PointerParVal^ := PointerParVal^.PointerComp^; end; (* with *) end; (* Proc1 *) procedure Proc2; (* (var IntParRef: OneToFifty) *) (* executed once *) (* InParRef = 3, becomes 7 *) var IntLoc: OneToFifty; EnumLoc: Enumeration; begin IntLoc := IntParRef + 10; repeat (* executed once *) if Char1Glob = 'A' then (* executed *) begin IntLoc := IntLoc - 1; IntParRef := IntLoc - IntGlob; EnumLoc := Ident1; end (* if *) until EnumLoc = Ident1; (* true *) end; (* Proc2 *) procedure Proc3; (* (var PointerParRef: RecordPointer) *) (* executed once *) (* PointerParRef becomes PointerGlob *) begin if PointerGlob <> nil then (* executed *) PointerParRef := PointerGlob^.PointerComp; Proc7 (10, IntGlob, PointerGlob^.IntComp); end; (* Proc3 *) procedure Proc4; (* without parameters *) (* executed once *) var BoolLoc: boolean; begin BoolLoc := Char1Glob = 'A'; BoolGlob := BoolLoc or BoolGlob; Char2Glob := 'B'; end; (* Proc4 *) procedure Proc5; (* without parameters *) (* executed once *) begin Char1Glob := 'A'; BoolGlob := false; end; (* Proc5 *) procedure Proc6; (* ( EnumParVal: Enumeration; var EnumParRef: Enumeration) *) (* executed once *) (* EnumParVal = Ident3, EnumParRef becomes Ident2 *) begin EnumParRef := EnumParVal; if not Func3 (EnumParVal) then (* not executed *) EnumParRef := Ident4; case EnumParVal of Ident1: EnumParRef := Ident1; Ident2: if IntGlob > 100 then EnumParRef := Ident1 else EnumParRef := Ident4; Ident3: EnumParRef := Ident2; (* executed *) Ident4: ; Ident5: EnumParRef := Ident3; end; (* case *) end; (* Proc6 *) procedure Proc7; (* ( Int1ParVal, Int2ParVal: OneToFifty; var IntParRef: OneToFifty) *) (* executed three times *) (* first call: Int1ParVal = 2, Int2ParVal = 3, *) (* IntParRef becomes 7 *) (* second call: Int1ParVal = 10, Int2ParVal = 5, *) (* IntParRef becomes 17 *) (* third call: Int1ParVal = 6, Int2ParVal = 10, *) (* IntParRef becomes 18 *) var IntLoc: OneToFifty; begin IntLoc := Int1ParVal + 2; IntParRef := Int2ParVal + IntLoc; end; (* Proc7 *) procedure Proc8; (* (var Array1ParRef: Array1DimInteger; var Array2ParRef: Array2DimInteger; Int1ParVal, Int2ParVal: integer) *) (* executed once *) (* Int1ParVal = 3 *) (* Int2ParVal = 7 *) var IntIndex, IntLoc: OneToFifty; begin IntLoc := Int1ParVal + 5; Array1ParRef [IntLoc] := Int2ParVal; Array1ParRef [IntLoc+1] := Array1ParRef [IntLoc]; Array1ParRef [IntLoc+30] := IntLoc; for IntIndex := IntLoc to IntLoc+1 do Array2ParRef [IntLoc, IntIndex] := IntLoc; Array2ParRef [IntLoc, IntLoc-1] := Array2ParRef [IntLoc, IntLoc-1] + 1; Array2ParRef [IntLoc+20, IntLoc] := Array1ParRef [IntLoc]; IntGlob := 5; end; (* Proc8 *) function Func1; (* (Char1ParVal, Char2ParVal: CapitalLetter): Enumeration *) (* executed three times, returns always Ident1 *) (* first call: Char1ParVal = 'H', Char2ParVal = 'R' *) (* second call: Char1ParVal = 'A', Char2ParVal = 'C' *) (* third call: Char1ParVal = 'B', Char2ParVal = 'C' *) var Char1Loc, Char2Loc: CapitalLetter; begin Char1Loc := Char1ParVal; Char2Loc := Char1Loc; if Char2Loc <> Char2ParVal then (* executed *) Func1 := Ident1 else (* not executed *) begin Char1Glob := Char1Loc; Func1 := Ident2; end; end; (* Func1 *) function Func2; (* (var String1ParRef, String2ParRef: String30): boolean *) (* executed once, returns false *) (* String1ParRef = 'DHRYSTONE PROGRAM, 1''ST STRING' *) (* String2ParRef = 'DHRYSTONE PROGRAM, 2''ND STRING' *) var IntLoc: OneToThirty; CharLoc: CapitalLetter; begin IntLoc := 2; while IntLoc <= 2 do (* loop body executed once *) if Func1 (String1ParRef[IntLoc], String2ParRef[IntLoc+1]) = Ident1 then (* executed *) begin CharLoc := 'A'; IntLoc := IntLoc + 1; end; (* if, while *) if (CharLoc >= 'W') and (CharLoc < 'Z') then (* not executed *) IntLoc := 7; if CharLoc = 'R' then (* not executed *) Func2 := true else (* executed *) begin if String1ParRef > String2ParRef then (* not executed *) begin IntLoc := IntLoc + 7; IntGlob := IntLoc; Func2 := true end else (* executed *) Func2 := false; end; (* if CharLoc *) end; (* Func2 *) function Func3; (* (EnumParVal: Enumeration): boolean *) (* executed once, returns true *) (* EnumParVal = Ident3 *) var EnumLoc: Enumeration; begin EnumLoc := EnumParVal; if EnumLoc = Ident3 then (* executed *) Func3 := true else (* not executed *) Func3 := false; end; (* Func3 *) begin (* main program, corresponds to procedures *) (* Main and Proc_0 in the Ada version *) (* Initializations *) new (NextPointerGlob); new (PointerGlob); PointerGlob^.PointerComp := NextPointerGlob; PointerGlob^.Discr := Ident1; PointerGlob^.EnumComp := Ident3; PointerGlob^.IntComp := 40; PointerGlob^.StringComp := 'DHRYSTONE PROGRAM, SOME STRING'; String1Glob := 'DHRYSTONE PROGRAM, 1''ST STRING'; Array2Glob [8,7] := 10; writeln; writeln ('Dhrystone Benchmark, Version 2.1 (Language: Pascal)'); writeln; writeln ('Please give the number of runs through the benchmark: '); readln (NumberOfRuns); writeln; writeln ('Execution starts, ', NumberOfRuns : 7, ' runs through Dhrystone'); BeginClock := clock; (***************) (* Start timer *) (***************) for RunIndex := 1 to NumberOfRuns do begin Proc5; Proc4; (* Char1Glob = 'A', Char2Glob = 'B', BoolGlob = false *) Int1Glob := 2; Int2Glob := 3; String2Glob := 'DHRYSTONE PROGRAM, 2''ND STRING'; EnumGlob := Ident2; BoolGlob := not Func2 (String1Glob, String2Glob); (* BoolGlob = true *) while Int1Glob < Int2Glob do (* loop body executed once *) begin Int3Glob := 5 * Int1Glob - Int2Glob; (* Int3Glob = 7 *) Proc7 (Int1Glob, Int2Glob, Int3Glob); (* Int3Glob = 7 *) Int1Glob := Int1Glob + 1; end; (* while *) (* Int1Glob = 3 *) Proc8 (Array1Glob, Array2Glob, Int1Glob, Int3Glob); (* IntGlob = 5 *) Proc1 (PointerGlob); for CharIndex := 'A' to Char2Glob do (* loop body executed twice *) if EnumGlob = Func1 (CharIndex, 'C') then (* not executed *) begin Proc6 (Ident1, EnumGlob); String2Glob := 'DHRYSTONE PROGRAM, 3''RD STRING'; Int2Glob := RunIndex; IntGlob := RunIndex; end; (* Int1Glob = 3, Int2Glob = 3, Int3Glob = 7 *) Int2Glob := Int2Glob * Int1Glob; Int1Glob := Int2Glob div Int3Glob; Int2Glob := 7 * (Int2Glob - Int3Glob) - Int1Glob; (* Int1Glob = 1, Int2Glob = 13, Int3Glob = 7 *) Proc2 (Int1Glob); (* Int1Glob = 5 *) end; (* for RunIndex *) EndClock := clock; (**************) (* Stop timer *) (**************) writeln ('Execution ends'); writeln; writeln ('Final values of the variables used in the benchmark:'); writeln; writeln ('IntGlob: ', IntGlob : 5); writeln (' should be: 5'); write ('BoolGlob: '); if BoolGlob = true then writeln ('TRUE') else writeln ('FALSE'); writeln (' should be: TRUE'); writeln ('Char1Glob: ', Char1Glob); writeln (' should be: A'); writeln ('Char2Glob: ', Char2Glob); writeln (' should be: B'); writeln ('Array1Glob [8]: ', Array1Glob [8] : 5); writeln (' should be: 7'); writeln ('Array2Glob [8,7]: ', Array2Glob [8,7] : 5); writeln (' should be: NumberOfRuns + 10'); writeln ('PointerGlob^.Discr: ', ord (PointerGlob^.Discr) : 5); writeln (' should be: 0'); writeln ('PointerGlob^.EnumComp: ', ord (PointerGlob^.EnumComp) : 5); writeln (' should be: 2'); writeln ('PointerGlob^.IntComp : ', PointerGlob^.IntComp : 5); writeln (' should be: 17'); write ('PointerGlob^.StringComp: '); for I := 1 to 30 do write (PointerGlob^.StringComp [I]); writeln; writeln (' should be: DHRYSTONE PROGRAM, SOME STRING'); writeln ('NextPointerGlob^.Discr: ', ord (NextPointerGlob^.Discr) : 5); writeln (' should be: 0'); writeln ('NextPointerGlob^.EnumComp: ', ord (NextPointerGlob^.EnumComp) : 5); writeln (' should be: 1'); writeln ('NextPointerGlob^.IntComp: ', NextPointerGlob^.IntComp : 5); writeln (' should be: 18'); write ('NextPointerGlob^.StringComp: '); for I := 1 to 30 do write (NextPointerGlob^.StringComp [I]); writeln; writeln (' should be: DHRYSTONE PROGRAM, SOME STRING'); writeln ('Int1Glob: ', Int1Glob : 5); writeln (' should be: 5'); writeln ('Int2Glob: ', Int2Glob : 5); writeln (' should be: 13'); writeln ('Int3Glob: ', Int3Glob : 5); writeln (' should be: 7'); writeln ('EnumGlob: ', ord (EnumGlob) : 5); writeln (' should be: 1'); write ('String1Glob: '); for I := 1 to 30 do write (String1Glob [I]); writeln; writeln (' should be: DHRYSTONE PROGRAM, 1''ST STRING'); write ('String2Glob: '); for I := 1 to 30 do write (String2Glob [I]); writeln; writeln (' should be: DHRYSTONE PROGRAM, 2''ND STRING'); writeln; writeln; SumClocks := EndClock - BeginClock; if SumClocks < TooSmallTime then begin writeln ('Measured time too small to obtain meaningful results'); writeln ('Please increase number of runs'); writeln; end else begin Microseconds := SumClocks * (MicrosecondsPerClock / NumberOfRuns); (* Brackets to prevent integer overflow *) DhrystonesPerSecond := NumberOfRuns * (ClocksPerSecond / SumClocks); write ('Microseconds for one run through Dhrystone: '); writeln (Microseconds : 8 : 1); write ('Dhrystones per Second: '); writeln (DhrystonesPerSecond : 8 : 1); writeln; end; end.