Mega Code Archive

 
Categories / Delphi / Examples
 

Optimize your code

Title: Optimize your code Question: A short guideline to check your knowledge in the heat of the night fight with your code Answer: Some tips and hints to optimize your code. Either code can be improved /extended or code can be reduced. Removing superfluous directives or data will make the code smaller and easier to follow. 1. one statement 2. with speed 3. assert call 4. class reference 5. try finally 6. naming convention 7. $IF-directive /include 8. var parameters 9. threadvar 10. uses list 11. database connection 12. message dialogs 13. open arrays 14. RTTI 15. getWin32 error ------------------------------------- 1. An easy one states that only one statement should exists for every source line, like next begin .. I := 5; CallProc(I); //bad //better begin .. I:= 5; CallProc(I); .. end; 2. Use of the With can increase considerably the speed in your programs since the sentences are executed quickly, enclosed between the begin and the end of the With; the compiler optimizes the chunk of code. But there can be a trap like an ambiguous references in with -blocks reports locations where a reference to a record field or class member exists in a with -statement, and where this identifier could be mixed up with another identifier in the same scope. var title: string; type TMyRec = record title: string; end; var Rec: TMyRec; begin .. with Rec do begin .. title := Hello'; end; end; 3. Use assert calls as much, but don't forget the $C - setting is active or not. This means that identifiers used in the Assert procedure call, will be registered in your tests, but when compiled by Delphi, this code line will be (should) stripped out if $C- is defined. procedure MyProc(P: pointer); begin Assert(P nil); The intention of using ASSERT is never to HALT, but to assert a condition for the subsequent program code. In other words: if a program is correct no ASSERT in it will ever fail (and neither will any type guard). This is why it is possible to drop the time consuming checks with a compiler option. assert(LEN(array) = maxItemCnt); 4. Use a Class Reference to define a actual class is determined at runtime. type TMyClassRef = class of TMyClass; TMyClass = class .. procedure AirMax; virtual; end; TMyDerivedClass = class(TMyClass) .. procedure AirMax; override; end; .. procedure Proc(c: TmyClassRef); begin c:= TMyClassRef.Create; end; 5. Find locations in your code with possible memory leaks, cause you forget try finally or an exit is in between: procedure ListMaxBox; var sL: TStringList; begin sL:= TStringList.Create; if cancel then Exit; // no way to free .. sL.Free; // missing try-finally block! end; 6. Use a naming convention like TBitBtn;bitn TButton;btn TCheckBox;chk TComboBox;cbo TRadioButton;rb TRadioGroup;rg ......... Ordinary types start with "T" Exception types start with "E" Pointer types tart with "P" Interface types start with "I" Class fields exposed by properties (read/write) start with "F" Method pointers start with "On/Before/After" 7. In Delphi 6, the new $IF-directive was introduced. The $IF-directive is followed by an expression, that evaluates to TRUE or FALSE. Use it to differentiate your code In Delphi there is also a compiler directive, {$INCLUDE}, that allows you to include a separate source file and insert it into your code. When the compiler gets to these lines, it just reads the lines in when it gets to each of the compiler directives. Now I just have 1 copy of the code that I need to maintain, but I gain the speed advantage of not having to put it in a procedure. Procedure DWSsocketCheck; begin A:= B * 10; {$INCLUDE dwssocketSpeed.INC} ..... ShowMessage('hold on the line, it's just fine'); end; An unnecessary action concerning strings is also next, since long strings are automatically initialized as empty strings upon creation. procedure MyMaxBoxProc; var S: string; begin S:= ''; // !! unnecessary or it is faster to check s[0] instead of function call StrLen 8. Avoid var parameters that are used, but never set never set in the subprogram they belong to. Although this is not an error, it may be an indication that something is wrong with your code. Otherwise, you may omit the var keyword, or change it to a const parameter. Declare with the const directive, resulting in better performance since the compiler can assume that the parameter will not be changed. procedure MyHexMaxProc(var i : integer); //const begin .. if i = 5 then // !! I is not set begin .. end; end; by the way: use not more than 3 parameters in a method, so the compiler can build it with fastcall directives, means registers are used instead of stack! 9. Beware of threadsafe. Reference -counted variables (such as long strings, dynamic arrays, or interfaces) are not thread-safe and should not be declared with threadvar: threadvar S: string; P: pointer; X: IMyInterface; W: TProcedure; 10. Optimize uses list, we know the linker is a smart one, but nevertheless init and finals are executed. Removing unused uses references has multiple benefits: - cleaner code to maintain, no need to bother about code that's not used - code from initialization and finalization sections in unused units is not linked in and run, reducing the size of the EXE - compilation runs smoother and quicker When you drop a VCL component on a Delphi form the Delphi IDE automatically adds the unit or units required by the component to the interface section uses statement. This is done to ensure that the form file (DFM/XFM) can locate the code needed to stream the form and components. Even if you later remove the component, the units are not deleted from the uses statement. 11. Database Connection It is considered bad practice to set a connection property of a database or query to True on a form. It's important to put some preconditions in the source before open a database! 12. Message Dialogs If a message dialog asks a question you should check the result: MessageDlg('are you sure?', mtConfirmation, [mbYes, mbNo],0); better: If MessageDlg() = mrNo then ... 13. open arrays A nice feature in OP is to use Open Arrays to pass an array where the routine doesn't know how much elements are in this array. This routine is capable of accepting arrays of several diff. lenghts, so reducing code size! function mypopRoutine(var a: array of integer): integer; var i: word; for i:= low(a) to high(a) do.... 14. RTTI Runtime Type Information enables Delphi to identify each instance of a class. Using is the runtime lib can check wheter or not an instance IS of a specific class type and while the AS can be used to do a RTTI typecast: if Sender IS TBitBtn then with Sender AS TBitBtn do... 15. Win32 Error There's an easy way to get almost every win32 error in a text message. With the helper function function SysErrorMessage(ErrorCode: Integer): string; from unit SysUtils you convert the int to text: Showmessage(SysErrorMessage(5)); For ex. the int 5 means "access denied" At least use Ctrl+0-O so you will see all the settings of the compiler directive in edit mode the same you get in the project's *.OPT file. ---------------------------- Many of those hints can be detected by a tool, but don't get me wrong, it's not a marketing meaning of myself. I'm talking about Pascal Analyzer and it's a great tool. Pascal Analyzer (also eyebol), or PAL for short, is a utility program that analyzes, documents, debugs, and helps you optimize your source code. Fore more is a testreport (in german) available on http://www.softwareschule.ch/download/pascal_analyzer.pdf Just a wish for a next Delphi Version is the function StoreObject() from TObject and the method storeToStream() as persistent! May the source be with you in 2008.