pyscripter 689 Posted October 26, 2019 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
Remy Lebeau 1396 Posted October 26, 2019 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. 1 1 Share this post Link to post
Stefan Glienke 2002 Posted October 26, 2019 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. 1 2 Share this post Link to post
David Heffernan 2345 Posted October 28, 2019 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
Stefan Glienke 2002 Posted October 29, 2019 (edited) 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 October 29, 2019 by Stefan Glienke 1 Share this post Link to post
David Heffernan 2345 Posted October 29, 2019 I don't follow that Stefan. Share this post Link to post