Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Benchmarking C Statements


FEB89: BENCHMARKING C STATEMENTS

David has been developing programmer's tools for several years. He is the chief scientist at Minimum Instruction Set Computer Inc. and can be reached at P.O. Box 1528, Golden, CO 80402.


Programmers will expend considerable (sometimes excessive) effort to reduce the execution times of their programs. Widely held maxims such as "pointers are faster than array indexing" or "multiply is faster than divide" don't always apply to a particular situation; any program might benefit from a little tool-aided exploration and the optimization of any code consuming a disproportionate share of CPU cycles. Most programmers go first to a profiler, which measures the fractions of the total execution time consumed by various parts of a program. Once the profiler has identified the time critical portions of code, the programmer can fine tune those parts with sbench.

Sbench is a program generator. It reads a list of C statements and creates a test program to measure the execution time of each statement individually. This allows a quick and easy comparison of the performances of various alternative methods of accomplishing a given task. Sbench also makes it much easier to explore the performance of a compiler in a variety of situations.

Sbench is of primary interest to programmers of real-time systems and others concerned with optimizing small regions of code. Limitations on the performance of a program imposed by the execution of a few lines of bottleneck code are not unusual. Furthermore, it is not always obvious where and when such bottlenecks occur.

Sbench

I was inspired to write sbench by Ralph E. Griswold's "Benchmarking Icon Expressions," (19 October 1987, The Icon Project, University of Arizona), which describes a similar tool for the Icon programming language. Icon presents a programmer with even more operators and alternatives than C, but the question of which statement would be faster arises often enough in C to warrant a tool to help answer it.

In order to be really useful a C statement benchmarking program must satisfy a number of requirements. First, it should be easy to use; that is, the program should require a minimum from the programmer. In sbench, often the only information required is a list of the statements to be compared. Sbench generates a test program from such a list and compiles, links, and executes it.

C programs, however, contain more than simple executable statements. In order to compare various ways of reading data from a file, for example, buffers and file descriptors must be declared and the file must be opened before any input statements can be executed. Some mechanism must be provided to allow for declaring variables (both global and local), including other files, and executing the initialization code required for the statements to be tested.

The most important requirement a C statement benchmarking program must satisfy, of course, is that it provided some way to measure the execution time of the statements under test. The draft ANSI C standard contains the library function clock(), which seems ideal for this purpose. clock() returns an implementation's best approximation of the processor time used by a program. Unfortunately, clock() is not universally available, even on compilers that claim to support ANSI C. In most MS-DOS implementations, the resolution of the clock function is 18.2 ticks per second, inadequate for timing individual statements that may take only a few microseconds to execute.

sbench solves both problems by providing a customized version of clock() with much higher timing resolution. Sbench's timing function can be implemented on the IBM PC and compatibles by reprogramming the 8253 timer chip. Byron Sheppard's "High-Performance Software Analysis on the IBM PC" (Byte, January 1987) details this programming of the 8253 chip. Note that this solution requires a high degree of hardware compatibility.

For other systems with low-resolution clock() functions, the statement under test can be enclosed in a loop and repeated many times. This increases the total time required to a value that can be measured with a low-resolution timer. Putting the statement under test inside a loop is not an ideal solution because it can lead to multiple side effects and increase the confusion an optimizing compiler can create. Sbench provides for both the higher-resolution version of clock() and the repeating loop solutions to the timing problem.

The final requirement of a C benchmarking tool is that its generated test programs should run on a wide variety of systems and compilers. The emerging ANSI C standard will make portability much easier to achieve in the future, but unfortunately, compiler support of the draft standard is uneven. Sbench's solution is to try to generate programs that avoid system specific features and provide for various implementations of C by use of #if conditional compilation directives. ANSI C is used in the version of sbench presented here, but "old style" C (K&R) alternatives are provided too. I have compiled the generated test programs with four different MS-DOS C compilers. I do not have access to other hardware, so portability to non-MS-DOS systems is untested.

The code for sbench itself does not maintain the level of compiler independence the programs it generates do. The sbench code uses several features introduced in the ANSI draft standard and not supported by most K&R C compilers. In particular, function prototypes and concatenation of adjacent string constants are used throughout the code. I have not added the conditional compilation switches needed to allow compilation with either K & R or ANSI style of compiler because the switches would have increased the size and decreased the readability of the code.

Benchmark programs rarely do anything more than print times, and consequently, they can be invalidated by sufficiently smart optimizing compilers. Sbench is susceptible to this problem. One solution is not to use the optimizer for the test code. This has the severe drawback, however, that the times will not correspond to the times in the real, optimized code. The best solution is to include enough realistic code in your test that the optimizer will make the same improvements it would in the actual application.

There are also some tricks you can use to prevent the optimizer from completely removing your test code. Few microcomputer C compilers optimize across function calls, so passing the results of any calculations to an external function will usually convince an optimizer that the results are being used. Also helpful in preventing the optimizer from removing the loops is to use the loop index variable in any calculation. Finally, note that some optimizers make fewer changes in code using global variables than in code with local variables.

Sbench Code

The code for sbench, the statement benchmark generator, is in Listing One, page 98. The operation of the program is quite simple -- sbench collects statements from the input into three linked lists, one for global declarations, one for local declarations, and one for the statements to be timed. Global lines are copied into the beginning of the generated test program, external to any function. Local lines are copied into the main function of the test program before any executable statements. Finally, the statements to be timed are copied into the test program. The three elements are combined with fragments of code to make a complete program. The additional code fragments required to complete the program are given by the pgm* string variables defined in lines 70 to 95.

Global lines must occur first in the input and are separated from local lines by a line beginning with %%. Local lines must precede any test statements and are indicated by a single initial % character. Any statement may be split over several physical lines by the usual C convention of ending lines to be continued with a \ character.

Once the test program is created, sbench attempts to compile and execute it with the system commands in lines 215 to 225. The programmer can use a command-line option (-g) to prevent compilation of the test program. Unless the programmer gives it another name with the -f option, the test program is called statbm.c. Sbench will not delete the test program so it may be (re)compiled if the programmer wants to compare various compilers and/or options.

Listing Two on page 101, contains the code for the routine getline( ), which reads a line of arbitrary length and combines continued physical lines into one logical line. Listing Three, page 101, contains the command-line option parsing routine getopt( ). getopt( ) is a Unix function, available in only some micro-computer C implementations. Because getopt() is not specified in the ANSI draft standard, I have included it here.

clock( ), the microsecond resolution clock function, is given in Listing Four, page 102. clock() is very hardware and compiler dependent. The code in Listing Four should be portable to IBM PC compatible systems with C compilers that have the ability to control interrupts and access hardware ports. Listing Four can be used as a guide for implementation on other systems. Its function has been described in detail in Byron Sheppard's Byte article.

Results

Listing Five(a), page 103, is an example of an input file for sbench. No global variables are used, so the global section is empty except for a comment. Note that in addition to variable declarations, the global section could contain #include commands, definitions of functions called by the test code, or any other valid C code. Several local variables are declared in lines 3 to 6. Executable code to perform required initialization could also be placed in the local section.

The first two test statements (Listing Five's lines 7 and 8) add two float and double variables, respectively. The results in Listing Five(b) were obtained using floating point software with the Zortech C compiler. They show clearly why float variables should not be used when speed is important. When the optimizer is used in compiling the test code, the results in Listing Five(c) are obtained. The execution time appears to have been reduced to almost zero -- the optimizer has recognized that the results of the additions are not used anywhere and has completely removed all the code generated by the additions. Inspection of the object code confirms this. The apparent results of 1 to 2 microseconds in Five(b) are due to just round off error and uncertainty in the timing function.

The remainder of the test input compares using subscripts and pointers for initializing and copying an array. Each statement tested is a for loop split into two or more lines to illustrate the use of the continuation character ( \). Using pointers for the unoptimized code is slightly faster for initializing and copying. The optimizer removes the advantage in the initialization loop but increases the advantage in the copy loop. The conclusion to be drawn from this is that micro performance will vary substantially depending on the exact details of the code. When tuning code for maximum performance, you have to test the specific code for your application. Sbench makes it easy to try a large number of variations.

Further

Like all tools, sbench doesn't do everything, and there are a few ways in which it could be improved or extended. One simple improvement would be to provide a mechanism for passing options from sbench's command line through to the compiler and/or to the test program itself when compiling the test program. Another would be to extend the program to generate test programs in other languages by providing alternate definitions of the code fragments in the pgm*strings --a fairly easy improvement to make. Finally, note that the idea of using programs to generate or manipulate other programs is extremely powerful. Code similar to sbench's could be used to insert commands in a test program for profiling by routine, block, or line.

A Caution

While the fine tuning encouraged by this tool has its place, don't get carried away by the notion of speed. Faster is not always better; readability, maintainability, and portability are at least as important as speed for most programs. As always, the programmer must weigh the importance of conflicting requirements.

_Benchmarking C Statements_ by David Fox

[LISTING ONE]

<a name="006c_000a">
  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| }





<a name="006c_000b"><a name="006c_000b">
<a name="006c_000c">
[LISTING TWO]
<a name="006c_000c">

  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| }






<a name="006c_000d"><a name="006c_000d">
<a name="006c_000e">
[LISTING THREE]
<a name="006c_000e">

  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| }






<a name="006c_000f"><a name="006c_000f">
<a name="006c_0010">
[LISTING FOUR]
<a name="006c_0010">

  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| }






<a name="006c_0011"><a name="006c_0011">
<a name="006c_0012">
[LISTING FIVE]
<a name="006c_0012">

  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













Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.