performanceactionscript-3memoryavm2

What are the negative impacts of extending classes in ActionScript 3?


In my game engine I use Box2D for physics. Box2D's naming conventions and poor commenting ruin the consistent and well documented remainder of my engine which is a little frustrating and presents poorly when you're using it.

I've considered making a set of wrapper classes for Box2D. That is, classes which extend each of the common Box2D objects and have their functions rewritten to follow the naming conventions of the rest of my engine, and to have them more clearly and consistently commented. I have even considered building ontop of some of the classes and adding some bits and pieces (like getters for pixel-based measurements in the b2Vec2 class).

This is fine but I am not 100% sure what the negative impacts of this would be and the degree to which those would affect my applications and games. I'm not sure if the compiler alleviates some of my concerns to a degree or whether I do need to be considerate when adding somewhat unnecessary classes for the sake of readability and consistency.

I have some suspicions:

I am asking specifically about runtime impacts.


Solution

  • This is a pretty common problem when it comes to integrating third party libraries, especially libraries that are ports (as Box2DAS3 is), where they keep the coding and naming conventions of the parent language rather than fully integrating with the destination language (case in point: Box2DAS3 using getFoo() and setFoo() instead of a .foo getter/setter).

    To answer your question quickly, no, there will be no significant performance impact with making wrapper classes; no more than you'll see in the class hierarchy in your own project. Sure, if you time a loop of 5 million iterations, you might see a millisecond or two of difference, but in normal usage, you won't notice it.

    "More memory consumption to accommodate the extra level of class structure." Like any language that has class inheritence, a vtable will be used behind the scenes, so you will have a small increase in memory/perf, but it's negligible.

    "Performance impact when creating new objects due to initializing an extra level of members?" No more than normal instantiation, so not something to worry about unless you're creating a huge amount of objects.

    Performance wise, you should generally have no problem (favour readability and usability over performance unless you actually have a problem with it), but I'd look at it more as an architectural problem and, with that in mind, what I would consider to be a negative impact of extending/modifying classes of an external library generally fall into 3 areas, depending on what you want to do:

    Modify the libary

    As Box2DAS3 is open source, there's nothing stopping you jumping in and refactoring all the class/function names to your hearts content. I've seriously considered doing this at times.

    Pros:

    Cons:

    Extend the classes

    Here, you simply make your own wrapper classes, which extend the base Box2D classes. You can add properties and functions as you want, including implementing your own naming scheme which translates to the base class (e.g. MyBox2DClass.foo() could simply be a wrapper for Box2DClass.bar())

    Pros:

    Cons:

    Composition with your own classes

    You make your own classes, which internally hold a Box2D property (e.g. MyPhysicalClass will have a property b2Body). You're free to implement your own interface as you wish, and only what's necessary.

    Pros:

    Cons:

    Out of the three, I prefer to go with composition, as it gives the most flexibility and keeps the modular nature of your engine intact, i.e. you have your core engine classes, and you extend functionality with external libraries. The fact that you can switch out libraries with minimal effort is a huge plus as well. This is the technique that I've employed in my own engine, and I've also extended it to other types of libraries - e.g. Ads - I have my engine Ad class, that can integrate with Mochi, Kongregate, etc as needed - the rest of my game doesn't care what I'm using, which lets me keep my coding style and consistency throughout the engine, whilst still being flexible and modular.

    ----- Update 20/9/2013 -----

    Big update time! So I went back to do some testing on size and speed. The class I used is too big to paste here, so you can download it at http://divillysausages.com/files/TestExtendClass.as

    In it, I test a number of classes:

    All tests were done in the standalone player, FP v11.7.700.224, Windows 7, on a not-great laptop.

    Test1: Size

    AS3 is a bit annoying in that if you call getSize(), it'll give you the size of the object itself, but any internal properties that are also Objects will just result in a 4 byte increase as they're only counting the pointer. I can see why they do this, it just makes it a bit awkward to get the right size.

    Thus I turned to the flash.sampler package. If we sample the creation of our objects, and add up all the sizes in the NewObjectSample objects, we'll get the full size of our object (NOTE: if you want to see what's created and the size, comment in the log calls in the test file).

    These sizes are all in bytes. Some points worth noting:

    So obviously the difference in size depends on the properties that you add, but all in all, pretty negligible.

    Test 2: Creation Speed

    For this, I simply used getTimer(), with a loop of 10000, itself looped 10 (so 100k) times to get the average. System.gc() was called between each set to minimise time due to garbage collection.

    There's not a whole pile to note here. The extending/composition classes take slightly longer, but it's like 0.000007ms (this is the creation time for 100,000 objects), so it's not really worth considering.

    Test 3: Call Speed

    For this, I used getTimer() again, with a loop of 1000000, itself looped 10 (so 10m) times to get the average. System.gc() was called between each set to minimise time due to garbage collection. All the objects had their getPosition()/GetPosition() functions called, to see the difference between overriding and redirecting.

    This one surprised me a bit, with the difference between the times being ~2x (though that's still 0.000007ms per call). The delay seems entirely down to the class inheritence - e.g. Box2DExtendsOverrides simply calls super.GetPosition(), yet is twice as slow as Box2DExtendsProperty, which inherits GetPosition() from its base class.

    I guess it has to do with the overhead of function lookups and calling, though I took a look at the generated bytecode using swfdump in the FlexSDK, and they're identical, so either it's lying to me (or doesn't include it), or there's something I'm missing :) While the steps might be the same, the time between them probably isn't (e.g. in memory, it's jumping to your class vtable, then jumping to the base class vtable, etc)

    The bytecode for var v:b2Vec2 = b2Body.GetPosition() is simply:

    getlocal        4
    callproperty    :GetPosition (0)
    coerce          Box2D.Common.Math:b2Vec2
    setlocal3
    

    whilst var v:b2Vec2 = Box2DExtends.getPosition() (getPosition() returns GetPosition()) is:

    getlocal        5
    callproperty    :getPosition (0)
    coerce          Box2D.Common.Math:b2Vec2
    setlocal3
    

    For the second example, it doesn't show the call to GetPosition(), so I'm not sure how they're resolving that. The test file is available for download if someone wants to take a crack at explaining it.

    Some points to keep in mind:

    All-in-all, I'd expect the same results from extending one of my own classes, so I wouldn't really worry about it. Implement the architecture that works the best for your solution.