Difference between revisions of "Avoiding implicit try finally section"
(Changed, as this issue is relevant with all types that must be initialized/finalized) |
|||
Line 2: | Line 2: | ||
E.g. procedure like | E.g. procedure like | ||
− | + | ==psudocode to explain the problem== | |
<pre> | <pre> | ||
procedure P; | procedure P; | ||
Line 28: | Line 28: | ||
Here's a link to archived discussion on fpc-devel list regarding this issue, with subject "TList slowness in classes" : http://www.mail-archive.com/fpc-devel@lists.freepascal.org/msg01367.html | Here's a link to archived discussion on fpc-devel list regarding this issue, with subject "TList slowness in classes" : http://www.mail-archive.com/fpc-devel@lists.freepascal.org/msg01367.html | ||
+ | note that ansistring temp vars can be created implicitly. The only real way to be sure whats going on is to read the asm output. (maybe the compier developers could add an option to generate a warning when an implicit try-finally is inserted). | ||
+ | ==possible soloutions== | ||
+ | split of rarely used code that causes an implicit try finally into seperate procedures. (you can use procedures in procedures) (verified) | ||
+ | |||
+ | use const parameters rather than value parameters. (avoids need to change refcount but temps could still be an issue). | ||
+ | |||
+ | use global vars. (have to be carefull with reentrency issues here though and temps could still be an issue). | ||
+ | |||
+ | use non refcounted types like shortstrings. | ||
+ | |||
+ | |||
+ | ===demonstraction split off rarely used code that causes implicit try finally=== | ||
And below is a small demo program that | And below is a small demo program that | ||
* When run, clearly shows that avoiding an implicit try ... finally block can make code a lot faster. When I run this program on my system, I get | * When run, clearly shows that avoiding an implicit try ... finally block can make code a lot faster. When I run this program on my system, I get |
Revision as of 05:22, 4 January 2005
Sometimes it's useful to know that compiler can wrap some code in implicit try ... finally block. Essentialy this is needed when you use variable of any type that must be initialized / finalized (i.e. standard procedures Initialize() and Finalize() do something meaningful with it), like AnsiStrings or Variants or dynamic arrays. Or (only in FPC earlier than 2004-12-26) resource strings.
E.g. procedure like
psudocode to explain the problem
procedure P; var S: AnsiString; begin ... do something with S ... end;
is actually compiled like
procedure P; var S: AnsiString; begin Initialize(S); try ... do something with S ... finally Finalize(S) end; end;
This is needed, to be sure that reference-count of S will be properly decremented when procedure P will exit with exception. But in some cases this can significantly affect the speed of given code.
Here's a link to archived discussion on fpc-devel list regarding this issue, with subject "TList slowness in classes" : http://www.mail-archive.com/fpc-devel@lists.freepascal.org/msg01367.html
note that ansistring temp vars can be created implicitly. The only real way to be sure whats going on is to read the asm output. (maybe the compier developers could add an option to generate a warning when an implicit try-finally is inserted).
possible soloutions
split of rarely used code that causes an implicit try finally into seperate procedures. (you can use procedures in procedures) (verified)
use const parameters rather than value parameters. (avoids need to change refcount but temps could still be an issue).
use global vars. (have to be carefull with reentrency issues here though and temps could still be an issue).
use non refcounted types like shortstrings.
demonstraction split off rarely used code that causes implicit try finally
And below is a small demo program that
- When run, clearly shows that avoiding an implicit try ... finally block can make code a lot faster. When I run this program on my system, I get
Time of Foo_Normal: 141 Time of Foo_Faster: 17
- Shows a trick how to avoid implicit try ... finally block (without changing the meaning or safety of the code) in some cases (when you don't need to actually use that AnsiString/Variant/something every time procedure is called but e.g. only if some parameter has some particular value).
{$mode objfpc}{$H+} uses {BaseUnix, Unix needed only to implement Clock} BaseUnix, Unix, SysUtils; function Clock: Int64; var Dummy: tms; begin Clock := FpTimes(Dummy); end; procedure Foo_Normal(i: Integer); var S: string; begin if i = -1 then begin S := 'Some operation with AnsiString'; raise Exception.Create(S); end; end; procedure Foo_Faster(i: Integer); procedure RaiseError; var S: string; begin S := 'Some operation with AnsiString'; raise Exception.Create(S); end; begin if i = -1 then RaiseError; end; { Note that when I call Foo_Normal and Foo_ResourceString i is always >= 0 so Exception is never actually raised. So string constants SNormal and SResString are not really used. } const TestCount = 10000000; var i: Integer; Start: Int64; begin Start := Clock; for i := 0 to TestCount do Foo_Normal(i); Writeln('Time of Foo_Normal: ', Clock - Start); Start := Clock; for i := 0 to TestCount do Foo_Faster(i); Writeln('Time of Foo_Faster: ', Clock - Start); end.