Performance penalty from inheritance - Forum - OpenEdge Development - Progress Community

Performance penalty from inheritance

 Forum

Performance penalty from inheritance

  • In a comment on OE Hive on this node http://www.oehive.org/node/1793 Guillaume Morin mentioned the performance penalty from inheritance.  This surprised me, so I asked for test code, which he provided.

    In his test he has two class hierarchies.  One has 5 private data members and five methods in the base class and no inheritance or imterfaces.  The other has one of the members in the leaf class, one in its parent, and three in the grandparent.  The five methods are all in the leaf class and reference interfaces.  The constructor for the child takes five arguments, calls a super passing up 4 values and assigns one to its member.  The class above it passes three in the super and assigns one.  And the grandparent assigns the remaining three values.

    Running a loop that creates 10000 of each of the first type and then 10000 of the leaf class of the second type on my machine (with no tuning etc) give 30.081s for the version without inheritance and 66.324s for the version with inheritance.

    I thought that the chained supers might be the issue so I created a second version.  In mine, I eliminated the methods and the interfaces, preferring to test that separately.  I changed all the private data members to public properties and eliminated all of the logic in the constructors.  The assignment of values to the properties was made in a separate assign statement following the NEW.

    That gave me 14.789s for the version with no inheritance and 47.619s for the version with inheritance.

    What's up???  The two leaf classes are functionally equivalent and the same logic is being used internally with both and externally with both.  The difference in definition should be resolved at compile time.  But, here we are seeing a 3X performance penalty!!!!

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • Another data point .... I just reran the test, but eliminated all the actual assignments.  The numbers were essentially the same.  So the big performance penaly is coming in the NEW, not in the access.  I suppose this is less disturbing since it only happens once per object, but this seems like an extreme variation.

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • tamhas wrote:

    Another data point .... I just reran the test, but eliminated all the actual assignments.  The numbers were essentially the same.  So the big performance penaly is coming in the NEW, not in the access.  I suppose this is less disturbing since it only happens once per object, but this seems like an extreme variation.

    At the risk of asking the obvious, what version are you running? And can you attach your test code?

    -- peter

  • 10.2B

    Here is Guillaume's original

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • And here is mine

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • In one case, 10,000 constructors are executed and in the other, 30,000 constructors are executed.

  • Are you saying that executing an empty constructor is the issue?  And that one actually has 30,000 objects instead of 10,000?

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • No, there are not 30,000 objects, there are 10,000.

    I understood your inheritance hiearchy to have 3 levels: leaf, parent, and

    grandparent. So there are 30,000 constructors, 3 per object.

    When you create a leaf object, its constructor is run. The leaf object class

    constructor runs the parent class constructor first, then the stuff you put

    in it. The parent constructor runs the grandparent constructor, etc. all the

    way up to the top of the hierarchy.

    Same with destructors.

    --

    regards,

    gus bjorklund, progress software

    If we wish to count lines of code, we should not regard them as lines

    produced but as lines spent. (Edsger Dijkstra)

  • And you are going to run all these constructors even though they are all empty?  And running the empty constructors is going to triple the time it takes to create the object?

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • Perhaps I misunderstood. I thought you have constructors. I did not examine

    your code nor run it. I only looked at the description of what was being

    done. I will take a look.

    You didn't think inheritance was free did you?

    Even if you have not written explicit constructors, there is still stuff

    that has to be done at the point where your constructor code would be

    executed. Data members and properties have to be allocated and initialised.

    Inherited constructors have to be run. I don't know exactly what the r-code

    has in it but will find out.

    --

    regards,

    gus bjorklund, progress software

    If we wish to count lines of code, we should not regard them as lines

    produced but as lines spent. (Edsger Dijkstra)

  • Each has a constructor, but the constructors are all empty.  They are all just

    constructor ClassA():

    end.

    Yes, the public properties in those superclasses obviously have to get instantiated, but there are exactly the same number of total properties in the two cases.  I.e., exactly the same amount of work needs to be done.  And yet, the inheritance version is taking much longer to do that work.

    I wouldn't have minded a small difference, but 3X seems like something is wrong.  Why would this not be something which was resolved at compile time?  Why would the leaf class in the inheritance hierarchy not be optimized to be identical in R-code to the case with no inheritance?  At runtime, there is no significance to the property coming from a parent.

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • For giggles I ran this on my home pc (the code from TMH's zip file). -l 50000 -mmax 65534 and everything run from .r code.

    With -q and with empty constructors:

    635
    1539

    With -q and without constructors:

    566
    1505

    Without -q and with empty constructors

    1181
    2892

    Without -q and without empty constructors

    1102
    2871

    There is a bit of overhead from just having the empty constructors  there, but it doesn't really add much.  -q has the greatest affect.

  • What is the difference ? This still shows the same ratio between with and without inheritance.

  • You are right.  My point was to make it clear that the empty constructors don't really make a difference.

  • Hi Thomas,

    We of course are always looking for ways to improve performance and class instantiation is one of those currently being investigated. That being said it was a design decision with OO to keep each class as a separate r-code file. The benefits are numerous, mostly around being able to change a class in a hierarchy without having to recompile. This is some of the beauty of the ABL where you can put a .p earlier on your propath to change behavior. To some extent you can do that in OO ABL. Java follows the same model where of course .NET tightly couples the hierarchy.

    So at compile time we look at the hierarchy and set up our dispatch table as appropriate and we keep a "digest" value for each r-code used. If at runtime the digest's match, we can quickly use the dispatch table (this is what happens with -q). If not, we need to redo the dispatch table at runtime which is something to avoid if possible.

    As far as runtime class instantiation, each class is run, the setup block is run (like block 0 for procedures) and then the contrsuctor is run which must immediatelty instatiation the super class and the same happens up the hierarchy. This is necessary with strong-typing. As I said we are looking at ways to improve this like maybe keep pools of object instances for reuse, etc.

    Let me know any suggestions/comments you might have.

    -Shelley