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
Common Subexpression Removal When compiling complex mathematical equations, Delphi will examine the equation to determine if any computations are performed more than once. If so, these common subexpressions are removed. Instead, Delphi will perform this calculation only once, and the resulting value will be used wherever the original calculation was located in the equation. The result is a mathematical computation that is reduced to a very compact, efficient form. This allows the developer to write computationally intensive code in a clear, concise manner without obscuring it by hand-optimizing the equation. Loop Induction Variables If a variable is used only as an index into an array or a string, Delphi will instead replace the variable with a pointer that is incremented to directly access the string or array values. This eliminates multiplication operations and increases the speed at which array or string values are accessed. Additionally, using an index that is of a data type native to the hardware (such as a 32-bit integer type) will improve performance. Dead Code Elimination While processing code, if Delphi detects that there is a block of code that is never called from the application, or code that ultimately has no effect, it will not be compiled into the final application. This is useful for reducing the size of the final build. However, during debugging, the compiler may remove code that would be useful to examine after a breakpoint. #### Additional Compiler Optimizations While these automatic compiler optimizations are quite handy, there are several other options available under the Project Options dialog box that can affect the behavior of an application. Aligned Record Fields When this option is turned on, all elements of record structures are adjusted so that they align on DWORD (32-bit) boundaries. The Intel architecture natively uses a 32-bit information format, and aligning the elements of a record so that each element begins at an address evenly divisible by 32 bits improves performance when accessing each element. The packed keyword can be used when declaring a record structure that will force alignment of the elements in that record structure to 32-bit boundaries.
| Caution: Bear in mind that this can change the size of a record structure, which is a problem when reading in information that was previously saved as non-aligned records. This may also have an effect on function calls to Windows or other third-party DLLs that take a record structure as a parameter.
Stack Frames When this option is checked on, the compiler will always pass parameters to functions on the stack, creating stack frames for every function and defeating the stack frame elimination optimization. This can be turned on for debugging purposes, but should always be turned off for the final build. Range Checking When this option is turned on, all array and string indexing expressions are verified as being within their defined bounds, and all assignments to scalar and subrange variables are checked to be within range. This generates additional code when accessing array and string elements, ultimately degrading application performance. Turn this option on only while debugging, and make sure it is off for the final build. Overflow Checking Similar to range checking, overflow checking controls the generation of overflow checking code for certain integer arithmetic operations. The code for each of these integer arithmetic operations is followed by additional code that verifies that the result is within the supported range. Thus, when this option is turned on, additional code is generated for every arithmetic operation performed, degrading performance. Again, turn this option on for debugging purposes, but turn it off for the final build.
| Tip: You can force any of these options on or off in your source code by including compiler directives. These directives, however, will override any settings made in this dialog box, which may cause confusion.
#### Other Delphi-Specific Optimization Techniques While these compiler optimizations help to improve overall application performance, there are several other tricks specific to Delphi that can dramatically increase application speed. Armed with a little knowledge about specific details about Delphi and the Object Pascal language, a developer can avoid common pitfalls that can completely negate the performance benefits realized by the compiler optimizations. Let’s examine some very specific techniques that can improve performance in a number of ways. Exceptions Exceptions are intended to inform both the user and the developer when something has gone wrong. Although they are very useful when employed correctly, they are incredibly slow. The developer should avoid trapping for exceptions at all costs, especially within a loop. The classic example is trapping for the EZeroDivide exception when performing division. If a number is divided by zero, it will raise an EZeroDivide exception. This could be put into a Try..Except block, simply swallowing the exception if it occurs. However, this is incredibly slow, and a simple example of this iterated as few as 10,000 times will take several seconds to execute on a Pentium 333 (on which the example was tested). If this Try..Except block is replaced by a simple If..Then to check if the divisor is zero before performing the math, 10,000 iterations take place in less than a millisecond (again, tested on a Pentium 333). Therefore, do not rely on exceptions for trapping errors when writing high-performance code. The following example demonstrates this technique.
| Note: You will need to turn off Stop on Delphi Exception for this example, located in Tools | Debugger Options | Language Exceptions.
Listing 10-2: Exception handling in a loop
    procedure TForm1.Button1Click(Sender: TObject);

    var

      iCount: Integer;

      Junk: Real;

      Divisor: Integer;

      StartTime,

      EndTime: LongInt;

    begin

      {make sure that optimization is off}

      {$O-}

      {begin recording the time}

      StartTime := timeGetTime;

      {perform a task that may or may not generate an exception (in this example

       it will always generate an exception). we don’t want the user to see this

       exception, so we programmatically handle it}

      Divisor := 0;

      for iCount := 0 to 10000 do

      begin

        try

          Junk := 10 / Divisor;

        except

          on EZeroDivide do;

        end;

      end;

      {recording the ending time...}

      EndTime := timeGetTime;

      {...and display the elapsed time}

      Label1.Caption := ‘Elapse Time (exception): ’+IntToStr(EndTime - StartTime);

      {begin recording the time}

      StartTime := timeGetTime;

      {perform the same task as before, but this time specifically check for

       instances that may create an exception}

      Divisor := 0;

      for iCount := 0 to 10000 do

      begin

        if Divisor <> 0 then

          Junk := 10 / Divisor;

      end;

      {record the ending time...}

      EndTime := timeGetTime;

      {...and display the elapsed time}

      Label2.Caption := ‘Elapse Time (if..then): ’+IntToStr(EndTime - StartTime);

    end;
| Tip: Try..Finally blocks incur little to no overhead, so it is safe to wrap high-performance code within a Try..Finally block.
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.