Tommi Prami 138 Posted Friday at 07:54 AM (edited) I was curious does the compiled optimize out inhetiance that is needlesly added. Turns out it can't. Is there any technical reason that this is not possible, how Delphi classes work, or it is just missing optimization from Delphi compiler., My point here is that, if you care, remove unneeded in heritance from ytou code base. Effect is very small, most likely not even measurable, but do as you please. In very tight loopå this might be porblem, most cases not. Why someone would add those, is different discussion. Sometimes I add overridden method, because I think I need to do something in there, but I just forget to remove it later. Test project: unit Unit27; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TForm27 = class(TForm) Button1: TButton; Memo1: TMemo; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; TBaseClass = class(TObject) strict private FList: TStringList; public constructor Create; destructor Destroy; override; end; TFoo1 = class(TBaseClass) strict private public constructor Create; destructor Destroy; override; end; TFoo2 = class(TFoo1) strict private public constructor Create; destructor Destroy; override; end; TFoo3 = class(TFoo2) strict private public constructor Create; destructor Destroy; override; end; TFoo4 = class(TFoo3) strict private public constructor Create; destructor Destroy; override; end; TFoo5 = class(TFoo4) strict private public constructor Create; destructor Destroy; override; end; TFoo6 = class(TFoo5) strict private public constructor Create; destructor Destroy; override; end; TFoo7 = class(TFoo6) strict private public constructor Create; destructor Destroy; override; end; TFoo8 = class(TFoo7) strict private public constructor Create; destructor Destroy; override; end; TFoo9 = class(TFoo8) strict private public constructor Create; destructor Destroy; override; end; TFoo10 = class(TFoo9) strict private public constructor Create; destructor Destroy; override; end; var Form27: TForm27; implementation {$R *.dfm} uses System.Diagnostics; procedure TForm27.Button1Click(Sender: TObject); const LOOP_COUNT = 1_000_000; var LSW: TStopwatch; begin LSW := TStopwatch.StartNew; for var I := 1 to LOOP_COUNT do begin var LObject := TBaseClass.Create; LObject.Free; end; LSW.Stop; Memo1.Lines.Add('BaseClass: ' + FormatFloat('0.00', LSW.Elapsed.TotalMilliseconds) + ' ms'); LSW := TStopwatch.StartNew; for var I := 1 to LOOP_COUNT do begin var LObject := TFoo10.Create; LObject.Free; end; LSW.Stop; Memo1.Lines.Add('unneeded inheritance: ' + FormatFloat('0.00', LSW.Elapsed.TotalMilliseconds) + ' ms'); end; procedure TForm27.FormCreate(Sender: TObject); begin end; { TBaseClass } constructor TBaseClass.Create; begin inherited Create; FList := TStringList.Create; end; destructor TBaseClass.Destroy; begin FList.Free; inherited Destroy; end; { TFoo9 } constructor TFoo9.Create; begin inherited Create; end; destructor TFoo9.Destroy; begin inherited Destroy; end; { TFoo1 } constructor TFoo1.Create; begin inherited Create; end; destructor TFoo1.Destroy; begin inherited Destroy; end; { TFoo2 } constructor TFoo2.Create; begin inherited Create; end; destructor TFoo2.Destroy; begin inherited Destroy; end; { TFoo3 } constructor TFoo3.Create; begin inherited Create; end; destructor TFoo3.Destroy; begin inherited Destroy; end; { TFoo4 } constructor TFoo4.Create; begin inherited Create; end; destructor TFoo4.Destroy; begin inherited Destroy; end; { TFoo5 } constructor TFoo5.Create; begin inherited Create; end; destructor TFoo5.Destroy; begin inherited Destroy; end; { TFoo6 } constructor TFoo6.Create; begin inherited Create; end; destructor TFoo6.Destroy; begin inherited Destroy; end; { TFoo7 } constructor TFoo7.Create; begin inherited Create; end; destructor TFoo7.Destroy; begin inherited Destroy; end; { TFoo8 } constructor TFoo8.Create; begin inherited Create; end; destructor TFoo8.Destroy; begin inherited Destroy; end; { TFoo10 } constructor TFoo10.Create; begin inherited Create; end; destructor TFoo10.Destroy; begin inherited Destroy; end; end. Edited Friday at 08:21 AM by Tommi Prami Added TRUE reson for message Share this post Link to post
David Heffernan 2372 Posted Friday at 07:57 AM The reason this is not optimised away is that it would take development effort to do so but would bring no benefit to anyone. 2 Share this post Link to post
Tommi Prami 138 Posted Friday at 08:19 AM 17 minutes ago, David Heffernan said: The reason this is not optimised away is that it would take development effort to do so but would bring no benefit to anyone. True, Benefit would be very small. Share this post Link to post
John R. 20 Posted Friday at 10:10 AM OT: I know that they shouldn't be compared but when I see the kind of efforts done to optimize many aspects of .NET versions after versions, giving it a very positive image to many (younger and older) developers, I can't see how Delphi could survive once most of us retire if it doesn't bring "sexy" optimizations and promote them. I don't say that this particular one should be implemented but would it be that costly for EMB to hire a full-time developer whose sole task would be to improve performances here and there, and actually blog about it, instead of hiding everything until release for whatever reason ? I believe that this could create a lot of positive feedback and could attract developers intrigued by this "new" Delphi language that they've never heard about 😉 1 1 Share this post Link to post
dummzeuch 1544 Posted Friday at 10:31 AM (edited) 2 hours ago, John R. said: I can't see how Delphi could survive once most of us retire if it doesn't bring "sexy" optimizations and promote them. [...] would it be that costly for EMB to hire a full-time developer whose sole task would be to improve performances here and there, and actually blog about it, instead of hiding everything until release for whatever reason ? Telling (the right) people about Delphi and how good it is (or rather: was), was something that has not been done since the very beginning. Not even Borland managed that. They advertised in Development magazines (and later websites), but not where those people who make decisions about the money to buy development tools (the C*Os of companies) would read about it. So when a developer asked his boss for the money to buy Delphi related tools, they had to tell him what it is (and who Borland, later Embarcadero is) and what the value would be for the money, because they had never heard these names. It's even worse nowadays: We see Embarcadero endlessly blubbing on about how great their tools are to developers, but not going into specifics. That's the way you talk to the C*Os of companies, not to developers. But the C*O people do not read these posts because they just don't visit embarcadero.com and the Delphi blogs. So so they still haven't heard these names when it comes to investment decisions. And the developers don't read them because they don't get any interesting information from them. Edited Friday at 12:36 PM by dummzeuch 1 Share this post Link to post
Stefan Glienke 2030 Posted Friday at 10:43 AM (edited) I am all for better optimization performed by the compiler but this particular case is not among them - if you write empty methods then use some static code analysis to let them be detected and remove them. Optimizing out empty methods (and here we are not talking about some regular methods but ctor and dtor!) is something that most compilers don't do for various reasons. Only when methods get inlined (which is not possible for virtual methods unless devirtualization is possible but that's an entirely different story) the compiler can detect that there actually is nothing to (and calling inherited is not nothing) and in fact the Delphi compiler does that - but ctor and dtor can not be inlined. The case you show here gets more interesting though if you remove all the empty ctors and dtors because then I still see a slight difference of 10-15%. The reason for that is TObject.InitInstance P.S. Just FYI if I run the benchmark without the empty ctor and dtor in Delphi 12 release config I get these numbers: BaseClass: 106,01 ms unneeded inheritance: 119,40 ms The same code compiled in Delphi 10.1 gives these numbers - so much about "there is no progress on optimization": BaseClass: 156,23 ms unneeded inheritance: 175,53 ms Although these particular differences are most likely because of the following two improvements I provided: Better TObject.InitInstance Better _FinalizeRecord Edited Friday at 11:00 AM by Stefan Glienke 2 Share this post Link to post
Tommi Prami 138 Posted yesterday at 05:30 AM On 2/7/2025 at 12:43 PM, Stefan Glienke said: I am all for better optimization performed by the compiler but this particular case is not among them - if you write empty methods then use some static code analysis to let them be detected and remove them. For sure. I just thought (or hoped) that this kind of unneeded stuff would be optimized out quite from the start. Curiocity came from, that I stumbled upon that kind of code, not that deep for sure as my example, just wanted to measure, and report the results here. -Tee- 1 Share this post Link to post
Tommi Prami 138 Posted yesterday at 05:32 AM On 2/7/2025 at 12:43 PM, Stefan Glienke said: P.S. Just FYI if I run the benchmark without the empty ctor and dtor in Delphi 12 release config I get these numbers: BaseClass: 106,01 ms unneeded inheritance: 119,40 ms The same code compiled in Delphi 10.1 gives these numbers - so much about "there is no progress on optimization": BaseClass: 156,23 ms unneeded inheritance: 175,53 ms That is quite nice. Thanks for info. 1 Share this post Link to post
Tommi Prami 138 Posted yesterday at 06:01 AM On 2/7/2025 at 12:43 PM, Stefan Glienke said: Although these particular differences are most likely because of the following two improvements I provided: Better TObject.InitInstance Better _FinalizeRecord Any optimization that makes Object/Recor "init/deinit" faster is surely good optimization. These will make almost mall apps faster. Other good candidates would be TList, TStrings, TDataSet (and descendants), just on top of my head. All in those would help in many places. Surely there are tons of places in RTL thatb would need some love... Share this post Link to post