Difference between revisions of "Avoiding implicit try finally section"

From Free Pascal wiki
Jump to navigationJump to search
m (Corrected italics, better wording)
(Changed, as this issue is relevant with all types that must be initialized/finalized)
Line 1: Line 1:
''Note: On 2004-12-26 Peter Vreman fixed this in compiler. This means that starting from FPC 1.9.5 2004-12-26, information on this page is no longer relevant. With newer compiler timings of demo program on this page show no significant difference between Test_ResourceString and Test_ResourceString_Fast.''
+
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.
  
''If you know of any other case where implicit try...finally section may be created and significantly affect speed of some code, please update this page. Otherwise, just ignore this page if you're using FPC newer than 1.9.5 from 2004-12-26.''
+
E.g. procedure like
  
Sometimes it's important to know that compiler can wrap some code in implicit try ... finally section. In some cases this can significantly affect the speed of given code.
+
<pre>
 +
procedure P;
 +
var S: AnsiString;
 +
begin
 +
... do something with S ...
 +
end;
 +
</pre>
 +
 
 +
is actually compiled like
 +
 
 +
<pre>
 +
procedure P;
 +
var S: AnsiString;
 +
begin
 +
Initialize(S);
 +
try
 +
  ... do something with S ...
 +
finally Finalize(S) end;
 +
end;
 +
</pre>
 +
 
 +
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
 
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
  
 
And below is a small demo program that
 
And below is a small demo program that
* Shows a trick how to avoid implicit try ... finally block (without changing the meaning or safety of the code) in some cases.
+
* 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, this program 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 Test_ResourceString:       107
+
  Time of Foo_Faster: 17
  Time of Test_ResourceString_Faster: 16
+
* 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).
  
 
<pre>
 
<pre>
{$mode objfpc}
+
{$mode objfpc}{$H+}
  
 
uses
 
uses
Line 26: Line 47:
 
end;
 
end;
  
resourcestring
+
procedure Foo_Normal(i: Integer);
  SResString = 'blah blah blah blah blah blah blah blah blah blah';
+
var S: string;
 
 
{ Note that Test_ResourceString and Test_ResourceString_Fast
 
  do exactly the same thing. }
 
 
 
procedure Test_ResourceString(i: Integer);
 
 
begin
 
begin
  if i = -1 then raise Exception.Create(SResString);
+
  if i = -1 then
 +
begin
 +
  S := 'Some operation with AnsiString';
 +
  raise Exception.Create(S);
 +
end;
 
end;
 
end;
  
procedure Test_ResourceString_Faster(i: Integer);
+
procedure Foo_Faster(i: Integer);
  
 
   procedure RaiseError;
 
   procedure RaiseError;
 +
  var S: string;
 
   begin
 
   begin
   raise Exception.Create(SResString)
+
  S := 'Some operation with AnsiString';
 +
   raise Exception.Create(S);
 
   end;
 
   end;
  
Line 48: Line 70:
 
end;
 
end;
  
{ Note that when I call Test_ResourceString and Test_ResourceString_Fast
+
{ Note that when I call Foo_Normal and Foo_ResourceString
   i is always >= 0 so Exception is never actually raised. }
+
   i is always >= 0 so Exception is never actually raised.
 +
  So string constants SNormal and SResString are not really used. }
  
 
const
 
const
Line 58: Line 81:
 
begin
 
begin
 
  Start := Clock;
 
  Start := Clock;
  for i := 0 to TestCount do Test_ResourceString(i);
+
  for i := 0 to TestCount do Foo_Normal(i);
  Writeln('Time of Test_ResourceString:       ', Clock - Start);
+
  Writeln('Time of Foo_Normal: ', Clock - Start);
+
 
 
  Start := Clock;
 
  Start := Clock;
  for i := 0 to TestCount do Test_ResourceString_Faster(i);
+
  for i := 0 to TestCount do Foo_Faster(i);
  Writeln('Time of Test_ResourceString_Faster: ', Clock - Start);
+
  Writeln('Time of Foo_Faster: ', Clock - Start);
 
end.
 
end.
 
</pre>
 
</pre>

Revision as of 00:28, 30 December 2004

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

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

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.