C TIPS(!) AND TRICKS(?) by Mark van den Boer
In this article I will explain some programming techniques which
are useful to C-programmers who like to code for speed. These
techniques (some call 'em tricks) are applicable on every machine
although some are 68000 specific.
The techniques will deal with individual functions and not with
the complete program. Naturally, if you optimize the most
important functions the complete program will execute faster.
The benefits of these optimization techniques are largely
dependent of the compiler you are using, since compilers differ
strongly in the way they optimize their code.
The techniques can be divided in a number of categories:
- General programming
These techniques can be used in every language.
When testing for certain conditions always test for the
condition that is most of the time true. E.g. if a variable lies
in 60% of all cases between 0 and 10, in 30% of all cases between
10 and 20 and in the other 10% it has another value, then first
test if the variable lies between 0 and 10, then test if the
variable lies between 10 and 20. In this case there is a 60%
chance you will only have to test once, which is of course faster
than testing multiple times.
When testing for some particular value, try to compare as many
times as possible with zero, since nearly each machine tests
faster for zero than for other values. E.g. if (i >= 1) could be
replaced by if (i > 0) which is more efficient.
Finally, I must admit when you really want speed you've got to
program in assembler. No good high level language programmer can
make a faster program than a good assembler programmer. E.g.
sprite and scrolling routines are best programmed in assembler.
It also must be mentioned that nearly every C-compiler allows the
programmer to call assembler routines. Making assembler written
functions accessible as if they were C-functions is a facility
most C packages support. This is achieved by putting the
assembler written function in a library using the librarian.
However, you should be aware of the function-calling conventions
used by your particular C-package.
- Arithmetic operations
Some integer arithmetic operations can be performed in more than
one way. Multiplication, division and modulo are a good example
of such operations. When performing one of these operations with
a power of 2 there is a faster way in C. Multiplication: var << 2
has the same result as var * 4 but is faster. Division: var >> 1
has the same result as var / 2 but is faster. Modulo: var & 16
has the same result as var % 16 but is faster. As a matter of
fact, even var << 4 + var << 6 is faster than var * 80 .
Use as little operations on floating point variables as you can,
since these operations are extremely time-consuming if your
computer hasn't got a mathematics coprocessor. Work hard to keep
away from floating points variables. E.g. if you have to deal
with percentages you could still use integer variables while
making it appear to the outer world as if you were using floating
points variables. This can be done by multiplying all percentages
with 100 and then do your calculations with these integers. When
printing the results you can do it as follows: Suppose variable i
contains 1234 which is equal to 12.34%. Then the following call
to printf will print it for you:
printf("%.2d.%.2d", i/100, i%100);
- Variables
C has a feature that is a gift from heaven for programmers who
code for speed. This feature is called register variables. This
is a special storage class which gives the compiler a strong hint
that the variable is heavily used. Even when accessing an array
which is declared as a global variable, put a pointer to the
array in a register variable and use the register variable for
successive references to the array.
Try to choose you variables as 'small' as possible. If you can do
the operation with a short int instead of an int then do it.
Same case for long and int .
Use as less floating point variables as possible for the reason
mentioned before.
To initialize an array of 1000 characters to zero see the
following function a fast way. Remember that an array of 4
characters in most C-implementations has the length of a long, so
to initialize all 4 characters to zero it is sufficient to
initialize the long to zero.
int fill(array)
register long *array;
{ register short i;
for (i = 249; i >= 0; ) array[i--] = 0L;
}
Use as much post-increment and pre-decrement on register pointers
as possible, since the 68000 has a very efficient way of dealing
with this sort of constructions. Thus, we could write the
previous example also as follows:
int fill(array)
register long *array;
{ register short i;
for (i = 249; i >= 0; ) *array++ = 0L;
}
Also, when frequently using a constant, put the constant in a
register, since operations on register variables are faster than
operations on constants.
- Control structures
One control structure which can be very strongly optimized is the
switch-statement. This is done by keeping all values of the case-
labels close to another. Example:
/* not optimal */
switch(var) {
case 1: func1(); break;
case 2: func2(); break;
case 3: func3(); break;
case 4: func4(); break;
case 1000: func1000(); break;
}
/* optimized version */
if (var == 1000) func1000();
else {
switch(var) {
case 1: func1(); break;
case 2: func2(); break;
case 3: func3(); break;
case 4: func4(); break;
}
}
Remember that C has the ?: operator. This can be very useful in
conditional function calls. Example:
/* not optimal */
if (var != 0) func("Take hold");
else func("of the flame");
/* optimal */
func( var !=0 ? "Take hold" : "of the flame" );
- Standard functions
Make use of the use standard Kernighan & Ritchie C-functions but
do it with care. One of the most used functions is undoubtedly
printf . There is one thing to remember about printf: it scans
the string character by character and internally builds another
string which it writes to the screen (standard output). So when
you want to write a string to the screen you should be aware that
puts also exists. puts doesn't have to build an internal string
which it can write to the screen, since its argument already
contains the string. Same is true for fprintf and fputs , scanf
and gets .
Disclaimer
The text of the articles is identical to the originals like they appeared
in old ST NEWS issues. Please take into consideration that the author(s)
was (were) a lot younger and less responsible back then. So bad jokes,
bad English, youthful arrogance, insults, bravura, over-crediting and
tastelessness should be taken with at least a grain of salt. Any contact
and/or payment information, as well as deadlines/release dates of any
kind should be regarded as outdated. Due to the fact that these pages are
not actually contained in an Atari executable here, references to scroll
texts, featured demo screens and hidden articles may also be irrelevant.