Jump to content
pyscripter

RttiContext Create and Free

Recommended Posts

The extended Rtti, available since Delphi XE, is great and has many uses, including serialization and exposing Delphi object to scripting languages.  There have been many discussions about whether you should keep a global RttiContext or not and whether you should call TRttiContext Create and Free every time you access extended Rtti.   For example this article suggests that keeping it in a global variable is a "very bad idea".  However there is a huge performance penalty if you do not do that.   For example in the following code Test1 runs about 100 times slower than Test2.  The reason is that in Test1, hundreds of Rtti objects are created and destroyed every time you access the rtti.

 

program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  System.Classes,
  System.Diagnostics,
  System.Rtti;

Const
  Iterations = 10000;
var
  SL : TStringList;

procedure Test1Inner;
begin
  var RttiContext := TRttiContext.Create;
  var RttiMethod := RttiContext.GetType(SL.ClassType).GetMethod('Clear');
  RttiMethod.Invoke(SL, []);
  RttiContext.Free;
end;

Procedure Test1;
begin
  for var I := 1 to Iterations do begin
    Test1Inner;
  end;
end;

Procedure Test2;
begin
  var RttiContext := TRttiContext.Create;
  for var I := 1 to Iterations do begin
    var RttiMethod := RttiContext.GetType(SL.ClassType).GetMethod('Clear');
    RttiMethod.Invoke(SL, []);
  end;
  RttiContext.Free;
end;

begin
  SL := TStringList.Create;
  try
   var SW := TStopwatch.StartNew;
   Test1;
   SW.Stop;
   WriteLn(SW.ElapsedMilliseconds.ToString);

   SW := TStopwatch.StartNew;
   Test2;
   SW.Stop;
   WriteLn(SW.ElapsedMilliseconds.ToString);
  finally
    SL.Free;
  end;
  ReadLn;
end.

There is a middle-of-the-road solution to the performance issue, once you realize that there can be many Rtti Contexts but there is just one Rtti Pool as @Stefan Glienke points out at the bottom of this Stackoverflow topic.  (By the way, the bug discussed in this question was fixed in Delphi Rio).  So instead of having a global RttiContext you can instead insert the following code at the bottom of one of your units.

Var
_RttiContext: TRttiContext;

procedure _InitRttiPool;
begin
 _RttiContext := TRttiContext.Create;
 _RttiContext.FindType('');
end;

initialization
  _InitRttiPool;
finalization
  _RttiContext.Free();

Note that _RttiContext is not used anywhere else, but this is enough to keep the rtti pool alive and prevent the performance penalty involved in creating and freeing the RttiContext.  @David Heffernan suggests something similar in the StackOverflow topic mentioned above but for a different reason.   The downside of course is the memory overhead involved in keeping all these rtti objects alive for the lifetime of the application.   Any comments?

Share this post


Link to post
3 minutes ago, pyscripter said:

The extended Rtti, available since Delphi XE

Since Delphi 2010, actually.

3 minutes ago, pyscripter said:

There have been many discussions about whether you should keep a global RttiContext or not and whether you should call TRttiContext Create and Free every time you access extended Rtti.

RTTI data is pooled in memory.  There is only 1 ref-counted copy of the RTTI data in memory that all active TRttiContext instances share.

3 minutes ago, pyscripter said:

For example this article suggests that keeping it in a global variable is a "very bad idea". 

That article is old, and not 100% accurate.  For instance, the implementation of TRttiContext.Create() shown in the article is how Create() was implemented in D2010, but it was changed in XE.

3 minutes ago, pyscripter said:

For example in the following code Test1 runs about 100 times slower than Test2.  The reason is that in Test1, hundreds of Rtti objects are created and destroyed every time you access the rtti.

Test1 is creating and destroying the RTTI pool 100 times, because it only has 1 TRttiContext instance alive at a time.  The pool's refcount never goes above 1.  The pool is recreated each time the refcount is incremented to 1, and destroyed when the refcount is decremented to 0.  That is not the case in Test2, which has 2 active TRttiContext instances alive at a time so the pool is not destroyed until after the loop is finished.

 

  • Like 1
  • Thanks 1

Share this post


Link to post

You only ever have to worry about dropping the TRttiContext when you unload modules such as dll and bpl during your application livetime from whom you needed rtti because then the type information from those modules is not removed and might still hang around possibly causing some issues.

  • Like 1
  • Thanks 2

Share this post


Link to post
On 10/26/2019 at 8:34 AM, Stefan Glienke said:

You only ever have to worry about dropping the TRttiContext when you unload modules such as dll and bpl during your application livetime from whom you needed rtti because then the type information from those modules is not removed and might still hang around possibly causing some issues.

You don't need to worry about even that because the dynamic memory associated with RTTI is ref counted and the refs will go to zero when the module is unloaded. 

Share this post


Link to post
On 10/28/2019 at 7:51 AM, David Heffernan said:

You don't need to worry about even that because the dynamic memory associated with RTTI is ref counted and the refs will go to zero when the module is unloaded. 

Eh? No, if I in my host application code keep a TRttiContext and keep around some of those TRtti* instances retrieved they will be or contain dangling references. The removal of TRtti* instances of an unloaded module is triggered from TRttiPool.GetPackageList which only triggers if I go through any methods of TRttiPool which is an internal class accessed by TRttiContext. Been there, suffered from that

Edited by Stefan Glienke
  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×