21 September 2023, 07:32 | #1 |
Registered User
Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 2,719
|
Using GenProto in SASC
I'm trying to compile a "DHRYSTONE" benchmark program written in 1984 that I found on Fish disk #378. When I turn register parameters on SASC says it can't do that because there are no 'prototypes' for the functions involved.
That sort of makes sense, so I use the 'GenProto' compile option to create a file that looks like this:- Code:
/* Prototypes for functions defined in dry.c */ int main(void); extern int IntGlob; extern boolean BoolGlob; extern char Char1Glob; extern char Char2Glob; extern Array1Dim Array1Glob; extern Array2Dim Array2Glob; extern RecordPtr PtrGlb; extern RecordPtr PtrGlbNext; int Proc0(void); int Proc1(RecordPtr PtrParIn); int Proc2(OneToFifty * IntParIO); int Proc3(RecordPtr * PtrParOut); int Proc4(void); int Proc5(void); int Proc6(Enumeration EnumParIn, Enumeration * EnumParOut); int Proc7(OneToFifty IntParI1, OneToFifty IntParI2, OneToFifty * IntParOut); int Proc8(Array1Dim Array1Par, Array2Dim Array2Par, OneToFifty IntParI1, OneToFifty IntParI2); Enumeration Func1(CapitalLetter CharPar1, CapitalLetter CharPar2); boolean Func2(String30 StrParI1, String30 StrParI2); boolean Func3(Enumeration EnumParIn); Here's the program code:- Code:
#include <stdio.h> #include "dry_protos.h" /* * "DHRYSTONE" Benchmark Program * */ /* Accuracy of timings and human fatigue controlled by next two lines */ #define LOOPS 50000 /* Use this for slow or 16 bit machines */ /*#define LOOPS 500000 /* Use this for faster machines */ /* Compiler dependent options */ #define NOENUM /* Define if compiler has no enum's */ #undef NOSTRUCTASSIGN /* Define if compiler can't assign structures */ /* define only one of the next three defines */ /*#define NOTIME /* Define if no time() function in library */ /*#define TIMES /* Use times(2) time function */ /*#define TIME /* Use time(2) time function */ /* define the granularity of your times(2) function (when used) */ #define HZ 60 /* times(2) returns 1/60 second (most) */ /*#define HZ 100 /* times(2) returns 1/100 second (WECo) */ #ifdef NOSTRUCTASSIGN #define structassign(d, s) memcpy(&(d), &(s), sizeof(d)) #else #define structassign(d, s) d = s #endif #ifdef NOENUM #define Ident1 1 #define Ident2 2 #define Ident3 3 #define Ident4 4 #define Ident5 5 typedef int Enumeration; #else typedef enum {Ident1, Ident2, Ident3, Ident4, Ident5} Enumeration; #endif typedef int OneToThirty; typedef int OneToFifty; typedef char CapitalLetter; typedef char String30[31]; typedef int Array1Dim[51]; typedef int Array2Dim[51][51]; struct Record { struct Record *PtrComp; Enumeration Discr; Enumeration EnumComp; OneToFifty IntComp; String30 StringComp; }; typedef struct Record RecordType; typedef RecordType * RecordPtr; typedef int boolean; #define NULL 0 #define TRUE 1 #define FALSE 0 #ifndef REG #define REG #endif extern Enumeration Func1(); extern boolean Func2(); #ifdef TIMES #include <sys/types.h> #include <sys/times.h> #endif main() { Proc0(); } /* * Package 1 */ int IntGlob; boolean BoolGlob; char Char1Glob; char Char2Glob; Array1Dim Array1Glob; Array2Dim Array2Glob; RecordPtr PtrGlb; RecordPtr PtrGlbNext; Proc0() { OneToFifty IntLoc1; REG OneToFifty IntLoc2; OneToFifty IntLoc3; REG char CharLoc; REG char CharIndex; Enumeration EnumLoc; String30 String1Loc; String30 String2Loc; register unsigned int i; #ifdef TIME long time(); long starttime; long benchtime; long nulltime; starttime = time(0); for (i = 0; i < LOOPS; ++i); nulltime = time(0) - starttime; /* Computes overhead of looping */ #endif #ifdef TIMES time_t starttime; time_t benchtime; time_t nulltime; struct tms tms; times(&tms); starttime = tms.tms_utime; for (i = 0; i < LOOPS; ++i); times(&tms); nulltime = tms.tms_utime - starttime; /* Computes overhead of looping */ #endif PtrGlbNext = (RecordPtr) malloc(sizeof(RecordType)); PtrGlb = (RecordPtr) malloc(sizeof(RecordType)); PtrGlb->PtrComp = PtrGlbNext; PtrGlb->Discr = Ident1; PtrGlb->EnumComp = Ident3; PtrGlb->IntComp = 40; strcpy(PtrGlb->StringComp, "DHRYSTONE PROGRAM, SOME STRING"); /***************** -- Start Timer -- *****************/ printf ("Start timer!\n"); fflush (stdout); #ifdef TIME starttime = time(0); #endif #ifdef TIMES times(&tms); starttime = tms.tms_utime; #endif for (i = 0; i < LOOPS; ++i) { Proc5(); Proc4(); IntLoc1 = 2; IntLoc2 = 3; strcpy(String2Loc, "DHRYSTONE PROGRAM, 2'ND STRING"); EnumLoc = Ident2; BoolGlob = ! Func2(String1Loc, String2Loc); while (IntLoc1 < IntLoc2) { IntLoc3 = 5 * IntLoc1 - IntLoc2; Proc7(IntLoc1, IntLoc2, &IntLoc3); ++IntLoc1; } Proc8(Array1Glob, Array2Glob, IntLoc1, IntLoc3); Proc1(PtrGlb); for (CharIndex = 'A'; CharIndex <= Char2Glob; ++CharIndex) if (EnumLoc == Func1(CharIndex, 'C')) Proc6(Ident1, &EnumLoc); IntLoc3 = IntLoc2 * IntLoc1; IntLoc2 = IntLoc3 / IntLoc1; IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1; Proc2(&IntLoc1); /***************** -- Stop Timer -- *****************/ } printf ("Stop timer!\n"); fflush (stdout); #ifdef TIME benchtime = time(0) - starttime - nulltime; printf("Dhrystone time for %ld passes = %ld\n", (long) LOOPS, benchtime); printf("This machine benchmarks at %ld dhrystones/second\n", ((long) LOOPS) / benchtime); #endif #ifdef TIMES times(&tms); benchtime = tms.tms_utime - starttime - nulltime; printf("Dhrystone time for %ld passes = %ld\n", (long) LOOPS, benchtime/HZ); printf("This machine benchmarks at %ld dhrystones/second\n", ((long) LOOPS) * HZ / benchtime); #endif } Proc1(PtrParIn) REG RecordPtr PtrParIn; { #define NextRecord (*(PtrParIn->PtrComp)) structassign(NextRecord, *PtrGlb); PtrParIn->IntComp = 5; NextRecord.IntComp = PtrParIn->IntComp; NextRecord.PtrComp = PtrParIn->PtrComp; Proc3(NextRecord.PtrComp); if (NextRecord.Discr == Ident1) { NextRecord.IntComp = 6; Proc6(PtrParIn->EnumComp, &NextRecord.EnumComp); NextRecord.PtrComp = PtrGlb->PtrComp; Proc7(NextRecord.IntComp, 10, &NextRecord.IntComp); } else structassign(*PtrParIn, NextRecord); #undef NextRecord } Proc2(IntParIO) OneToFifty *IntParIO; { REG OneToFifty IntLoc; REG Enumeration EnumLoc; IntLoc = *IntParIO + 10; for(;;) { if (Char1Glob == 'A') { --IntLoc; *IntParIO = IntLoc - IntGlob; EnumLoc = Ident1; } if (EnumLoc == Ident1) break; } } Proc3(PtrParOut) RecordPtr *PtrParOut; { if (PtrGlb != NULL) *PtrParOut = PtrGlb->PtrComp; else IntGlob = 100; Proc7(10, IntGlob, &PtrGlb->IntComp); } Proc4() { REG boolean BoolLoc; BoolLoc = Char1Glob == 'A'; BoolLoc |= BoolGlob; Char2Glob = 'B'; } Proc5() { Char1Glob = 'A'; BoolGlob = FALSE; } extern boolean Func3(); Proc6(EnumParIn, EnumParOut) REG Enumeration EnumParIn; REG Enumeration *EnumParOut; { *EnumParOut = EnumParIn; if (! Func3(EnumParIn) ) *EnumParOut = Ident4; switch (EnumParIn) { case Ident1: *EnumParOut = Ident1; break; case Ident2: if (IntGlob > 100) *EnumParOut = Ident1; else *EnumParOut = Ident4; break; case Ident3: *EnumParOut = Ident2; break; case Ident4: break; case Ident5: *EnumParOut = Ident3; } } Proc7(IntParI1, IntParI2, IntParOut) OneToFifty IntParI1; OneToFifty IntParI2; OneToFifty *IntParOut; { REG OneToFifty IntLoc; IntLoc = IntParI1 + 2; *IntParOut = IntParI2 + IntLoc; } Proc8(Array1Par, Array2Par, IntParI1, IntParI2) Array1Dim Array1Par; Array2Dim Array2Par; OneToFifty IntParI1; OneToFifty IntParI2; { REG OneToFifty IntLoc; REG OneToFifty IntIndex; IntLoc = IntParI1 + 5; Array1Par[IntLoc] = IntParI2; Array1Par[IntLoc+1] = Array1Par[IntLoc]; Array1Par[IntLoc+30] = IntLoc; for (IntIndex = IntLoc; IntIndex <= (IntLoc+1); ++IntIndex) Array2Par[IntLoc][IntIndex] = IntLoc; ++Array2Par[IntLoc][IntLoc-1]; Array2Par[IntLoc+20][IntLoc] = Array1Par[IntLoc]; IntGlob = 5; } Enumeration Func1(CharPar1, CharPar2) CapitalLetter CharPar1; CapitalLetter CharPar2; { REG CapitalLetter CharLoc1; REG CapitalLetter CharLoc2; CharLoc1 = CharPar1; CharLoc2 = CharLoc1; if (CharLoc2 != CharPar2) return (Ident1); else return (Ident2); } boolean Func2(StrParI1, StrParI2) String30 StrParI1; String30 StrParI2; { REG OneToThirty IntLoc; REG CapitalLetter CharLoc; IntLoc = 1; while (IntLoc <= 1) if (Func1(StrParI1[IntLoc], StrParI2[IntLoc+1]) == Ident1) { CharLoc = 'A'; ++IntLoc; } if (CharLoc >= 'W' && CharLoc <= 'Z') IntLoc = 7; if (CharLoc == 'X') return(TRUE); else { if (strcmp(StrParI1, StrParI2) > 0) { IntLoc += 7; return (TRUE); } else return (FALSE); } } boolean Func3(EnumParIn) REG Enumeration EnumParIn; { REG Enumeration EnumLoc; EnumLoc = EnumParIn; if (EnumLoc == Ident3) return (TRUE); return (FALSE); } #ifdef NOSTRUCTASSIGN memcpy(d, s, l) register char *d; register char *s; int l; { while (l--) *d++ = *s++; } #endif /* * Library function for compilers with no time(2) function in the * library. */ #ifdef NOTIME long time(p) long *p; { /* CI-C86 time function - don't use around midnight */ long t; struct regval {unsigned int ax,bx,cx,dx,si,di,ds,es; } regs; regs.ax = 0x2c00; sysint21(®s, ®s); t = ((regs.cx>>8)*60L + (regs.cx & 0xff))*60L + (regs.dx>>8); if (p) *p = t; return t; } #endif |
21 September 2023, 08:18 | #2 | |
Registered User
Join Date: Aug 2010
Location: Germany
Posts: 536
|
Quote:
|
|
22 September 2023, 05:47 | #3 |
Registered User
Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 2,719
|
Thanks for that. After posting I thought about it and figured that was probably the reason - and it was! It also had problems with strcmp and strcpy which I fixed by writing prototypes for them. Wonder why SASC didn't pick them up?
I then tried to figure out what the code was actually doing, as the description is vague and there are no comments in the actual code. I didn't know anything about Dhrystone except that it is an 'integer' benchmark. Turns out it does a bunch of things including string copy and compare, a 5 line switch block, and 1 and 2 dimensional array manipulations, as well as some very simple integer and boolean math. Wikipedia says that Dhrystone is 'susceptible to compiler optimizations', and it is correct. Compiling under SACS 6.58 with register variables and optimized for speed, the code executed 3.5 times faster than the register based executable included in the archive! That's a huge difference purely due to the compiler. I can imagine Amiga users being sorely disappointed by how 'slow' their machine was according to this benchmark, when it was actually the compiler that was holding it back! |
23 September 2023, 11:51 | #4 | |||
Registered User
Join Date: Aug 2010
Location: Germany
Posts: 536
|
Quote:
Quote:
This benchmark has been around since the early 1980'ies in several flavours, if memory serves. Hence, the manner in which it stirs the soup (clockwise or counterclockwise) may differ between platform/implementation languages with the original author perhaps being less than comfortable with the 'C' environment, compared to Pascal or FORTRAN. In the early 1980'ies the state of the art of the 'C' language documentation was still the first edition "The 'C' programming language", which did not even document the full runtime library. You were ahead of the pack if you were familiar with the Unix programming environment (documented in a separate book by half of the 'C' team). Quote:
Enabling 'C' compiler optimizations is cheating. So is enabling FORTRAN compiler optimizations. So is running the benchmark on a machine which features code and/or data caches. So is running the benchmark on a machine which uses a different instruction set architecture, pipelining, memory hierarchy, ABI, etc. |
|||
26 September 2023, 15:02 | #5 | |
Registered User
Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 2,719
|
Quote:
|
|
26 September 2023, 21:18 | #6 |
Registered User
Join Date: Dec 2010
Location: Athens/Greece
Age: 53
Posts: 722
|
|
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
SASC - Hello inutition world? | elwis | Coders. General | 6 | 28 January 2018 20:15 |
Sasc | swampcretin | request.Apps | 2 | 29 May 2005 14:52 |
|
|