home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
drdobbs
/
1989
/
02
/
fox.asc
< prev
next >
Wrap
Text File
|
1989-01-04
|
23KB
|
547 lines
_Benchmarking C Statements_
by David Fox
[LISTING ONE]
1| /* sbench.c -- C statement benchmark generator. */
2| /* Released to the public domain by David L. Fox, 1988. */
3|
4| /* This program will read a list of C statements and generate a test
5| program to time the execution of each of the statements in the list.
6| The list should have the form:
7| zero or more global declarations
8| %%
9| %zero or more local declarations or statements
10| one or more test statements
11| The global and local declarations and statements are not timed.
12| Every local statement must have a % in column 1. Every test statement
13| should be on one logical line. Physical lines ending with \ are
14| continued, as in C, so long logical lines may be split.
15|
16| The program is run with a command of the form:
17| sbench [ -cx ] [ -f name ] [ -n xx ] [ -o ] [ -g ] [ files ... ]
18| If no input files are given sbench reads the standard input.
19| Available options are:
20| -c x Select compiler: x=a Aztec, x=d Datalight,
21| x=e Ecosoft, x=t Turbo, x=z Zortech
22| -f name Write generated program to file name.
23| -n xx Execute each statement xx times.
24| -o Optimize when compiling benchmark.
25| -g Generate test program only, do not try to compile.
26| */
27| #include <stdio.h>
28| #include <stdlib.h>
29| #include <string.h>
30| #include <ctype.h>
31|
32| #define OUTFNAME "statbm.c" /* Default name of generated program. */
33|
34| struct listel { /* One element in a linked list of lines. */
35| struct listel *next; /* Link to next element. */
36| char *line; /* Pointer to the text of the line. */
37| };
38| struct llist {
39| struct listel *head; /* Head of the list. */
40| struct listel *tail; /* Tail of the list. */
41| };
42|
43| /* The following enum type is used to identify the compilers used. */
44| enum cctag { aztec = 'a', datalight = 'd', ecosoft = 'e',
45| turboc = 't', zortech = 'z'};
46| struct cmdline { /* Contains parts of command lines for compilers. */
47| enum cctag tag; /* Identifies compiler. */
48| char *cmd; /* Beginning of command line. */
49| char *optimize; /* Command line option to invoke optimizer. */
50| char *lib; /* Libraries added to end of command line. */
51| /* The libraries named should contain the high resolution */
52| /* clock() function if it is used. The exact contents of the */
53| /* lib strings will depend on how your hard disk is organized. */
54| } compilers[] = { /* Compilers[0] is the default. */
55| { aztec, "c -lx -lm -dMu_CLOCK", "+f", "" },
56| { datalight, "dlc -dMu_CLOCK", "-o", "\\lib\\dlc\\xs.lib" },
57| { ecosoft, "ecc -dMu_CLOCK -lecox", "", "" },
58| { turboc, "tcc -dMu_CLOCK", "-G", "\\lib\\tc\\xs.lib" },
59| { zortech, "ztc -dMu_CLOCK", "-o", "\\lib\\ztc\\xs.lib" }
60| };
61|
62| struct cmdoptns { /* Information extracted from command line. */
63| unsigned int ntimes; /* Repeat count for SUT. */
64| char *outfname; /* Name of output file. */
65| int doopt; /* Flag, non-zero optimizes test program. */
66| struct cmdline *compiler; /* Pointer to command line details. */
67| } options;
68|
69| /* These strings form parts of the generated program. */
70| char *pgmincl = "#include\t<stdio.h>\n#include\t<time.h>\n\n";
71| char *pgmhdr = "#ifdef\tMu_CLOCK\n" /* Using high-res clock()? */
72| "#undef\tCLK_TCK\n#define\tCLK_TCK\t1000000L\n#endif\n"
73| "#ifdef\t__STDC__\n" /* __STDC__ indicates an ANSI */
74| "int main(int argc, char **argv) {\n" /* conforming implementation */
75| "\tvoid sb_dummy(unsigned int);\n" /* with prototypes. */
76| "#else\n" /* otherwise use K&R style function definitions. */
77| "int main(argc, argv)\nint argc;\nchar **argv; {\n\tint sb_dummy();\n"
78| "#endif\n\tclock_t sb_t0, sb_time, sb_empty;\n"
79| "\tunsigned int sb_index, sb_nexpr, sb_niter;\n";
80| char *pgmend =
81| "\treturn 0;\n}\n#ifdef\t__STDC__\nvoid sb_dummy(unsigned int i) {\n"
82| "#else\nint sb_dummy(i)\nunsigned int i; {\n#endif\n\treturn;\n}\n";
83| char *pgminit = "clock();\n" /* Start clock, avoid first call overhead. */
84| "sb_nexpr = 0;\nsb_niter = %u;\n";
85| char *pgmstart = "sb_t0 = clock();\n";
86| char *pgmloop = "for (sb_index=0; sb_index < sb_niter; ++sb_index) {\n"
87| "sb_dummy(sb_index);\n"; /* Keep optimizers from gutting loop. */
88| char *pgmnoloop = "{\n"; /* Put expression to be timed in a block. */
89| char *pgmempty = "}\nsb_empty = clock() - sb_t0;\n"; /* Time empty stmt */
90| char *pgmstop = "}\nsb_time = clock() - sb_t0;\n" /* Print results. */
91| "printf(\"Statement %u required %6.1f microseconds to execute, "
92| "%u iteration%s timed.\\n\",\n++sb_nexpr,"
93| "(((double)(sb_time-sb_empty))*(1000000./CLK_TCK))/sb_niter, sb_niter,"
94| "sb_niter > 1 ? \"s\" : \"\");\n";
95|
96| /* Function prototypes. */
97| void docmdline(int argc, char **argv, struct cmdoptns *optptr);
98| char *getline(FILE *);
99| int getopt(int argc, char *argv[], char *);
100| void lladd(struct llist *last, char *str);
101| void outerr(void);
102| void writelines(struct llist *list, FILE *outfile);
103| void usage(void);
104|
105| /* External variables communicate with getopt(). */
106| extern int optind; /* Index of next command line argument in argv. */
107| extern char *optarg; /* Argument for a command line option (-o arg). */
108| int
109| main(int argc, char **argv) {
110| char cmdbuf[256], *str;
111| FILE *infile, *outfile;
112| struct llist *llp,
113| globall, /* Global declaration lines. */
114| locall, /* Local declaration lines. */
115| testl; /* Lines to be tested. */
116| struct listel *lep;
117|
118| /* Set up default values */
119| infile = stdin;
120| options.outfname = OUTFNAME;
121| options.ntimes = 1;
122| options.doopt = 0; /* Don't optimize test code. */
123| options.compiler = &compilers[0];
124| docmdline(argc, argv, &options); /* Process command line options. */
125|
126| globall.head = globall.tail = locall.head = locall.tail =
127| testl.head = testl.tail = NULL;
128|
129| /* Once through the loop for each input file. */
130| do {
131| if (optind < argc) {
132| /* Try to open a file from the command line. */
133| if ((infile = fopen(argv[optind], "r")) == NULL) {
134| perror(argv[optind]);
135| fprintf(stderr, "Failed to open input file\n");
136| continue; /* Try the next file. */
137| }
138| }
139| else {
140| /* There are are no files on the command line, */
141| /* use the standard input. */
142| infile = stdin;
143| if (isatty(fileno(stdin)))
144| fprintf(stderr, "Enter list of statements:\n");
145| }
146|
147| /* Now read the file and append its contents to
148| a linked list of lines. */
149| llp = &globall; /* First section contains global statements. */
150| while ((str = getline(infile)) != NULL) {
151| if (strncmp(str, "%%", 2) == 0)
152| llp = &testl; /* Found end of global section. */
153| else if (*str == '%')
154| lladd(&locall, str+1); /* This is a local declaration. */
155| else lladd(llp, str);
156| }
157| if (ferror(infile)) { /* Getline returns NULL on error or EOF. */
158| perror(argv[optind]);
159| fprintf(stderr, "Read error\n");
160| exit(1);
161| }
162|
163| if (fclose(infile)) {
164| perror(argv[optind]);
165| fprintf(stderr, "Error closing input file!\n");
166| exit(1);
167| }
168| } while (++optind < argc);
169|
170| /* Done with input, begin output phase. */
171| if ((outfile = fopen(options.outfname, "w")) == NULL) {
172| perror(options.outfname);
173| fprintf(stderr, "Can't create output file\n");
174| exit(1);
175| }
176|
177| /* Select and write the pieces of the generated program. */
178| if (fprintf(outfile, "%s", pgmincl) < 0) outerr();
179| writelines(&globall, outfile); /* Write global lines. */
180|
181| if (fprintf(outfile, "%s", pgmhdr) < 0) outerr(); /* Main program. */
182| writelines(&locall, outfile); /* Write local lines. */
183|
184| /* Write code to initialize variables. */
185| if (fprintf(outfile, pgminit, options.ntimes) < 0) outerr();
186|
187| /* Write code to time execution of an empty expression. */
188| if (fprintf(outfile, "%s", pgmstart) < 0) outerr();
189|
190| if (options.ntimes > 1) { /* Need looping code? */
191| if (fprintf(outfile, "%s", pgmloop) < 0) outerr();
192| }
193| else {
194| if (fprintf(outfile, "%s", pgmnoloop) < 0) outerr();
195| }
196| if (fprintf(outfile, "%s", pgmempty) < 0) outerr();
197|
198| /* Write code to time execution of statements. */
199| for (lep = testl.head; lep; lep = lep->next) {
200| if (fprintf(outfile, "%s", pgmstart) < 0) outerr();
201| if (options.ntimes > 1) { /* Need looping code? */
202| if (fprintf(outfile, "%s", pgmloop) < 0) outerr();
203| }
204| else {
205| if (fprintf(outfile, "%s", pgmnoloop) < 0) outerr();
206| }
207| /* Here is where the expression is written into the program. */
208| if (fprintf(outfile, "%s", lep->line) < 0) outerr();
209|
210| if (fprintf(outfile, "%s", pgmstop) < 0) outerr(); /* Output */
211| }
212| if (fprintf(outfile, "%s", pgmend) < 0) outerr(); /* Wrap up. */
213| if (fclose(outfile)) outerr();
214|
215| if (options.compiler != NULL) {
216| /* Compile and execute test program. */
217| sprintf(cmdbuf,"%s %s %s %s", options.compiler->cmd,
218| options.doopt ? options.compiler->optimize : "",
219| options.outfname, options.compiler->lib);
220| if (system(cmdbuf)) { /* MS-DOS doesn't return error codes. */
221| fprintf(stderr, "Compilation error\n");
222| exit(1);
223| }
224| *strchr(options.outfname, '.') = '\0'; /* Chop off ".exe" */
225| system(options.outfname);
226| }
227| return 0;
228| }
229|
230| /* lladd -- Add a new line to the tail of a linked list. */
231| void
232| lladd(struct llist *list, char *str) {
233| struct listel *lep;
234|
235| /* Create a new list structure. */
236| if ((lep = (struct listel *)malloc(sizeof(struct listel))) == NULL) {
237| fprintf(stderr, "Not enough memory\n");
238| exit(1);
239| }
240| lep->line = str; /* Add the line to it */
241| lep->next = NULL; /* and mark it as the tail. */
242| if (list->tail != NULL)
243| list->tail->next = lep; /* Link it to the old tail. */
244| list->tail = lep; /* Make it the new tail. */
245| if (list->head == NULL)
246| list->head = lep; /* First element is head. */
247| return;
248| }
249|
250| /* docmdline -- Extract options from command line. */
251| void
252| docmdline(int argc, char **argv, struct cmdoptns *optptr) {
253| int c, i;
254| enum cctag tag;
255|
256| while ((c = getopt(argc, argv, "c:f:n:og")) != EOF) {
257| switch(c) {
258| case 'c': /* Select compiler. */
259| tag = tolower(*optarg);
260| for (i = 0; i < sizeof(compilers)/sizeof(compilers[0]); ++i) {
261| if (tag == compilers[i].tag) { /* Is this the one? */
262| optptr->compiler = &compilers[i];
263| break;
264| }
265| }
266| if (i >= sizeof(compilers)/sizeof(compilers[0])) {
267| /* Didn't find compiler. */
268| fprintf(stderr, "Unknown compiler: %s\n", optarg);
269| usage();
270| }
271| break;
272| case 'f': /* Name of generated program from command line. */
273| if (NULL == (optptr->outfname = malloc(strlen(optarg)+1))) {
274| fprintf(stderr, "Not enough memory\n");
275| exit(1);
276| }
277| strcpy(optptr->outfname, optarg);
278| break;
279| case 'g': /* Don't compile. */
280| optptr->compiler = NULL;
281| break;
282| case 'n': /* Execute statement n times. */
283| optptr->ntimes = strtol(optarg, NULL, 0);
284| break;
285| case 'o': /* Use optimizer when compiling benchmark. */
286| optptr->doopt = 1;
287| break;
288| default:
289| usage();
290| }
291| }
292| }
293|
294| void
295| usage(void) { /* Print help message and exit. */
296| fprintf(stderr, "Usage: sbench [ options ] [ files ... ]\n"
297| " Available options are:\n"
298| " -c x Select compiler: x=a Aztec, x=d Datalight,\n"
299| " x=e Ecosoft, x=t Turbo, x=z Zortech\n"
300| " -f name Write generated program to file name.\n"
301| " -n xx Execute each statement xx times.\n"
302| " -o Optimize when compiling benchmark.\n"
303| " -g Generate test program only, do not try to compile.\n");
304| exit(1);
305| }
306|
307| /* writelines -- Output all lines in a list. */
308| void
309| writelines(struct llist *list, FILE *outfile) {
310| struct listel *lep;
311|
312| for (lep = list->head; lep != NULL; lep = lep->next) {
313| if (fprintf(outfile, "%s\n", lep->line) < 0) outerr();
314| }
315| return;
316| }
317|
318| /* outerr -- Display a message and abort following an output error. */
319| void
320| outerr(void) {
321| perror(options.outfname);
322| fprintf(stderr, "Error writing output file");
323| exit(1);
324| }
[LISTING TWO]
1| /* getline.c -- Read a line from a stream. */
2| #include <stdio.h>
3| #include <stdlib.h>
4| #include <string.h>
5|
6| #define MEMINCR 256 /* Size of memory block used for (m|re)alloc. */
7| /* getline -- Read one line from file infile into a malloc'ed string.
8| Lines ending with \ are combined with the following line.
9| Return NULL on error or EOF, otherwise a pointer to the string. */
10| char *
11| getline(FILE *infile)
12| { char *p, *q;
13| int c;
14| unsigned avail;
15|
16| p = q = malloc((unsigned)MEMINCR);
17| avail = MEMINCR - 1;
18| for (;;) {
19| if ((c = getc(infile)) == EOF) {
20| *p = '\0';
21| if (p > q) return q;
22| else return NULL;
23| }
24| if ((*p++ = c) == '\n') {
25| if (p <= q+1 || p[-2] != '\\') {
26| *p = '\0';
27| return q;
28| } /* else Continued line. */
29| }
30| if (--avail == 0) {
31| /* Need more memory. */
32| *p = '\0';
33| if ((q = realloc(q, (size_t)(p-q + MEMINCR + 1))) == NULL)
34| return NULL;
35| avail = MEMINCR;
36| p = strchr(q, '\0'); /* Find end of string, in case it moved. */
37| }
38| }
39| }
[LISTING THREE]
1| /* getopt.c -- UNIX-like command line option parser. */
2| #include <stdio.h>
3| #include <string.h>
4| int optind; /* Index of next argument in argv[]. */
5| char *optarg; /* Pointer to option argument. */
6|
7| /* getopt -- Returns option letters one at a time,
8| EOF when no more options remain, and ? for unknown option.
9| Optstr contains legal option letters. A colon following a
10| letter indicates that option requires an argument. */
11| int
12| getopt(int argc, char *argv[], char *optstr)
13| { static char *cp;
14| char *p;
15|
16| if(optind == 0)
17| cp = argv[++optind] + 1; /* First call */
18| if(*argv[optind] != '-' || optind >= argc)
19| return EOF; /* No more options. */
20| if(*cp == '-')
21| { ++optind;
22| return EOF; /* -- indicates end of options */
23| }
24| if((p = strchr(optstr, *cp++)) == NULL)
25| { fputs("unknown option: ", stderr);
26| putc(*(cp-1), stderr);
27| putc('\n', stderr);
28| if(*cp == '\0')
29| cp = argv[++optind] + 1;
30| return '?';
31| }
32| if(*(p+1) == ':')
33| { if(*cp == '\0') /* Get argument. */
34| optarg = argv[++optind];
35| else
36| optarg = cp;
37| cp = argv[++optind] + 1;
38| }
39| else if(*cp == '\0')
40| cp = argv[++optind] + 1; /* Set up for next argv. */
41| return *p;
42| }
[LISTING FOUR]
1| /* clock.c -- Microsecond resolution clock routine. */
2| /* Implements in C the timer chip tweaking described by */
3| /* Byron Sheppard, _Byte_, Jan 1987, p 157-164. */
4| /* Replaces standard clock() from the library. */
5| /* The definition of CLK_TCK in time.h may have to */
6| /* be changed to 1000000L. */
7| /* Does not correctly handle intervals spanning */
8| /* midnight or intervals greater than about 6 hrs. */
9| #include <time.h>
10|
11| /* Interrupt handling and i/o ports are compiler dependent. */
12| /* The following set of preprocessor directives selects the */
13| /* correct include files and macros for various compilers. */
14| #ifdef __ZTC__
15| #include <dos.h>
16| #include <int.h>
17| #define inportb inp
18| #define outportb outp
19| #else
20| #ifdef __TURBOC__
21| #include <dos.h>
22| #define int_off disable
23| #define int_on enable
24| #else
25| #error Unknown compiler
26| #endif
27| #endif
28|
29| /* Constants */
30| #define CONTVAL 0x34 /* == 00110100 Control byte for 8253 timer. */
31| /* Sets timer 0 to 2-byte read/write, mode 2, binary. */
32| #define T0DATA 0x40 /* Timer 0 data port address. */
33| #define TMODE 0x43 /* Timer mode port address. */
34| #define BIOS_DS 0x40 /* BIOS data segment. */
35| #define B_TIKP 0x6c /* Address of BIOS (18.2/s) tick count. */
36| #define SCALE 10000 /* Scale factor for timer ticks. */
37| /* The following values assume 18.2 BIOS ticks per second resulting from
38| the 8253 being clocked at 1.19 MHz. */
39| #define us_BTIK 54925 /* Micro sec per BIOS clock tick. */
40| #define f_BTIK 4595 /* Fractional part of micro sec per BIOS tick. */
41| #define us_TTIK 8381 /* Micro sec per timer tick * SCALE. (4/4.77 MHz) */
42|
43| clock_t
44| clock(void) {
45| unsigned char msb, lsb;
46| unsigned int tim_ticks;
47| static int init = 0;
48| unsigned long count, us_tmp;
49| static unsigned long init_count;
50|
51| if (0 == init) {
52| init = 1; /* This is the first call, have to set up timer. */
53| int_off();
54| outportb(TMODE, CONTVAL); /* Write new control byte to timer. */
55| outportb(T0DATA, 0); /* Initial count = 0 = 65636. */
56| outportb(T0DATA, 0);
57| init_count = *(unsigned long int far *)MK_FP(BIOS_DS, B_TIKP);
58| int_on();
59| return 0; /* First call returns zero. */
60| }
61| int_off(); /* Don't want an interrupt while getting time. */
62| outportb(TMODE, 0); /* Latch count. */
63| lsb = inportb(T0DATA); /* Read count. */
64| msb = inportb(T0DATA);
65| /* Get BIOS tick count (read BIOS ram directly for speed and
66| to avoid turning on interrupts). */
67| count = *(unsigned long far *)MK_FP(BIOS_DS, B_TIKP) - init_count;
68| int_on(); /* Interrupts back on. */
69| tim_ticks = (unsigned)-1 - ((msb << 8) | lsb);
70| us_tmp = count*us_BTIK;
71| return us_tmp + ((long)tim_ticks*us_TTIK + us_tmp%SCALE)/SCALE;
72| }
[LISTING FIVE]
1| /* Test data for statement benchmark generator. */
2| %%
3| %float f, g = 10., h = 20.;
4| %double c, d = 10., e = 20.;
5| %int i = 10, j, *p, *q;
6| %int array[50], another[50];
7| f = g+h; /* Float addition. */
8| c = d+e; /* Double addition */
9| /* Initialize an array using subscripting. */\
10| for (i=0; i < 50; ++i)\
11| array[i] = 0;
12| /* Initialize an array using pointer. */\
13| for (p = array; p < array + 50; ++p)\
14| *p = 0;
15| /* Copy using array subscripts. */\
16| for (i=0; i < 50; ++i)\
17| array[i] = another[i];
18| /* Copy using pointers. */\
19| for (p = array, q=another; p < array + 50; ++p, ++q)\
20| *p++ = *q++;
21| (a)
22| Test input
23|
24| Statement 1 required 1215.0 microseconds to execute, 1 iteration timed.
25| Statement 2 required 560.0 microseconds to execute, 1 iteration timed.
26| Statement 3 required 1671.0 microseconds to execute, 1 iteration timed.
27| Statement 4 required 1606.0 microseconds to execute, 1 iteration timed.
28| Statement 5 required 1820.0 microseconds to execute, 1 iteration timed.
29| Statement 6 required 1794.0 microseconds to execute, 1 iteration timed.
30| (b)
31| Results
32|
33| Statement 1 required 2.0 microseconds to execute, 1 iteration timed.
34| Statement 2 required 1.0 microseconds to execute, 1 iteration timed.
35| Statement 3 required 813.0 microseconds to execute, 1 iteration timed.
36| Statement 4 required 813.0 microseconds to execute, 1 iteration timed.
37| Statement 5 required 1112.0 microseconds to execute, 1 iteration timed.
38| Statement 6 required 777.0 microseconds to execute, 1 iteration timed.
39| (c)
40| Results with optimizer on