Tricks of the Windows Game Programming Gurus:Algorithms, Data Structures, Memory Management, and Multithreading

All ITKnowledge Source Code Search Tips Advanced Search |

| Tricks of the Windows Game Programming Gurus by Andre LaMothe Sams, Macmillan Computer Publishing ISBN: 0672313618 Pub Date: 09/27/99

Search this book:

Previous | Table of Contents | Next

#### Using Your Head

The first key to writing optimized code is understanding the compiler, the data types, and how your C/C++ is finally transformed into executable machine language. The idea is to use simple programming and simple data structures. The more complex and contrived your code is, the harder it will be for the compiler to convert it into machine code, and thus the slower it will execute (in most cases). Here are some basic rules to keep in mind:

  • Use 32-bit data as much as possible. 8-bit data may take up less space, but Intel processors like 32-bit data and are optimized to access it.
  • Use inline functions for small functions that you call a lot.
  • Use globals as much as possible, without making ugly code.
  • Avoid floating-point numbers for addition and subtraction because the integer unit is generally faster for these operations.
  • Use integers whenever possible. Although the floating-point processor is almost as fast as the integer processor, integers are exact. So if you don’t need decimal accuracy, use integers.
  • Align all data structures to 32-byte boundaries. You can do this manually, with compiler directives on most compilers, or within code using `#pragmas`.
  • Never pass data to functions as `value` if it is anything other than a simple type. Always use a pointer.
  • Don’t use the `register` keyword in your code. Although Microsoft says it makes faster loops, it starves the compiler of registers and ends up making the worst code.
  • If you’re a C++ programmer, classes and virtual functions are okay. Just don’t go crazy with inheritance and layers of software.
  • The Pentium-class processors use an internal data and code cache. Be aware of this, and try to keep the size of your functions relatively small so they can fit into the cache (16KB-32KB+). In addition, when you’re storing data, store it in the way it will be accessed. This will minimize cache thrashing and main memory or secondary cache access, which is 10 times slower than the internal cache.
  • Be aware that Pentium class processors have RISC-like cores, so they like simple instructions and allow two or more instructions to execute in more than one execution unit. Don’t write contrived code on a single line; it’s better to write simpler code that’s longer, even though you can mash the same functionality into the same line.

#### Mathematical Tricks

Because a great deal of game programming is mathematical in nature, it pays to know better ways to perform math functions. There are a number of general tricks and methods that you can use to speed things up:

  • Always use integers with integers and floats with floats. Conversion from one to another kills performance. Hence, hold off on the conversion of data types to the very last minute.
  • Integers can be multiplied by any power of two by shifting to the left. And likewise, they can be divided by any power of two by shifting to the right. Multiplication and division by numbers other than powers of two can be accomplished by using sums or differences of shifts. For example, 640 is not a power of two, but 512 and 128 are, so here’s the best way to multiply a number by 640 using shifts:

      product=(n<<7) + (n<<9); // n*128 + n*512 = n*640

  However, if the processor can multiply in 1-2 cycles, this optimization is worthless.

  • If you use matrix operations in your algorithms, make sure to take advantage of their sparseness—that is, zero entries.
  • When you’re creating constants, make sure that they have the proper casts so that the compiler doesn’t reduce them to integers or interpret them incorrectly. The best idea is to use the new C++ `const` directive. For example:

      const float f=12.45;

  • Avoid square roots, trigonometric functions, or any complex mathematical functions. In general, there is always a simpler way to do it by taking advantage of certain assumptions or making approximations. However, if worse comes to worst, you can always make a look-up table, which I’ll get to later.
  • If you have to zero out a large array of floats, use a `memset()`, like this:

      memset((void  *)float_array,0,sizeof(float)*num_floats);

  However, this is the only time that you can do this, because floats are encoded in IEEE format and the only integer and float values that are the same are 0.

  • When you’re performing mathematical calculations, see if you can reduce the expressions manually before coding them. For example, `n*(f+1)/n` is equivalent to `(f+1)` because the multiplication and division of `n` cancel out.
  • If you perform a complex mathematical operation and you know you’ll need it again a few lines down, cache it. For example:

      // compute term that is used in more than one expression

      float n_squared = n*n;

      // use term in two different expressions

      pitch = 34.5*n_squared+100*rate;

      magnitude = n_squared / length;

  • Last, but not least, make sure to set the compiler options to use the floating-point processor and create code that is fast rather than small.

Previous | Table of Contents | Next

Copyright © Macmillan Computer Publishing, Inc.

Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.