Search Tips Advanced Search |
| Delphi Graphics and Game Programming Exposed with DirectX 7.0 by John Ayres Wordware Publishing, Inc. ISBN: 1556226373 Pub Date: 12/01/99
Search this book:
Previous | Table of Contents | Next
#### Effective Optimization Knowing where to begin is probably the hardest part about optimizing code. In general, don’t worry about optimizing short pieces of code that are not called very often in the application. For example, if a few lines of code (or even one line) takes two seconds to process but is only called a few times during the entire life of the application, it is not worth examining. If possible, this type of code should be placed where a user will not mind the wait. In the typical game application, this usually involves the loading of game resources such as graphics and sound. This type of code should be executed when an application is starting and a splash screen is being displayed. Code of this nature can also be executed when changing levels or starting a level. Typically, the best place to begin optimization is within the innermost part of loops. Take, for example, the following pseudo-code:
      for iCount := 0 to 10 do

      begin

        {outer loop code}

        for iCount2 := 0 to 100 do

        begin

          {inner loop code}

        end;

      end;
If the outer loop code took a total of 100 milliseconds to execute for one iteration, excluding the inner loop, it would take a total of 1000 milliseconds, or one full second, for the entire loop to process (iterating 10 times). If the inner loop code took a total of 10 milliseconds to execute one iteration, it would take a total of 1000 milliseconds to process (iterating 100 times). Since the outer loop code takes longer to execute than the inner loop code (excluding the fact that the inner loop is executed with the outer loop code), one might be tempted to try optimizing the outer loop. The grand total execution time required to process this example is approximately 11 seconds (1 full second for the inner loop, executed 10 times (1 * 10), plus 1 more second for the outer loop). Even though the outer loop takes longer to process than the inner loop, if the total execution time of the outer loop was reduced to even 1 millisecond, the entire process will still take more than 10 seconds to execute. Now, if the inner loop execution time was cut in half (from 10 milliseconds to 5), the total execution time would be reduced from 11 seconds to approximately 6 seconds. That’s a big savings. As this example illustrates, the biggest performance enhancements will usually come from optimizing code performed in the innermost loops.
| Tip: Typical culprits, and places to begin looking for optimization opportunities, usually include loops that redraw the entire screen a pixel at a time as well as manager loops that perform specific actions on each item in a list.
Finally, knowing when to quit is almost as important as knowing where to start. Optimization gurus state that any code, however optimized, could always be optimized further. However, there is a diminishing margin of returns encountered at a certain level of optimization. For example, say a developer spends 10 hours optimizing code, decreasing its execution time by 100 milliseconds. The time spent was probably worth it. Now, if it takes another 5 or 10 hours to reduce the execution time by another 10 milliseconds, the developer’s time could probably be better spent elsewhere. At some point, execution time will only be dropping by a few milliseconds, and a decision will need to be made about whether or not the time spent performing the optimization is worth the performance increases. ### Delphi-Specific Optimization Techniques Delphi itself offers a number of very useful optimization options. The compiler performs quite a few automatic optimizations. There are several other options available that also affect how Delphi generates machine code. All of these options are available on the Compiler tab of the Project Options dialog, accessed using Project| Options. Figure 10-1: Project options
| Caution: If you are not familiar with these settings or have never used them before, Delphi may act in a manner that might be quite unfamiliar. Get to know these settings and how they affect Delphi’s overall performance and behavior.
#### Automatic Compiler Optimizations The very first check box under Code Generation is the Optimization option. Clicking this box on activates a number of automatic compiler optimization techniques. Unfortunately, with this setting, it’s an all-or-nothing affair. Turning this option off for debugging purposes is useful, but always turn this option on before performing a final build. When this option is on, it allows the compiler to perform a number of heavy-duty optimizations that can dramatically affect the performance of code. Some of these optimizations include register optimizations, call stack elimination, common subexpression removal, loop induction variables, and dead code elimination. Register Optimization If Delphi determines that a particular variable is used heavily, it will store that variable in a hardware register. This eliminates the need to access the value of the variable by loading it from memory, resulting in much faster code. Delphi takes this one step further by determining if certain variables are used extensively in different parts of code. If so, a single register can be used for each variable. Call Stack Elimination Delphi offers a number of different calling conventions that affect how parameters are passed to a function. Different calling conventions are required based on the task being accomplished, such as calling functions stored in C or C++ DLLs, or using Windows API functions. However, by default, Delphi uses a calling convention known as register. The register calling convention will pass up to three parameters to a function by using hardware registers. This makes it unnecessary to create a stack frame, eliminating quite a bit of initialization and cleanup code and improving application performance. However, if more than three parameters are passed, registers cannot be used and a stack frame will be created. Bear in mind that while a normal function can pass up to three parameters and still use this optimization, object methods can only pass two parameters, as Self is automatically passed as the third parameter. If a function requires more than three parameters, one way to still benefit from this optimization is to create a record structure defining all of the parameters. A single pointer to this record can then be passed to the function, allowing the function to access all of the required information while passing only one parameter in a register.
| Tip: A stack is also generated when a function returns more than a 32-bit number, such as returning a string. Try to write functions that don’t return anything more than a single 32-bit number.
Previous | Table of Contents | Next

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 whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.