Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>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 <code>getFoo()</code> and <code>setFoo()</code> instead of a <code>.foo</code> getter/setter).</p> <p>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.</p> <p><em>"More memory consumption to accommodate the extra level of class structure."</em> 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.</p> <p><em>"Performance impact when creating new objects due to initializing an extra level of members?"</em> No more than normal instantiation, so not something to worry about unless you're creating a huge amount of objects.</p> <p>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:</p> <ul> <li>Modify the library</li> <li>Extend the classes</li> <li>Composition with your own classes</li> </ul> <p><strong>Modify the libary</strong></p> <p>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.</p> <p><em>Pros:</em></p> <ul> <li>You can modify what you want - functions, classes, you name it</li> <li>You can add any missing pieces that you need (e.g. pixel-meters conversion helpers)</li> <li>You can fix any potential performance issues (I've noticed a few things that could be done better and faster if they were done in an "AS3" way)</li> </ul> <p><em>Cons:</em></p> <ul> <li>If you plan to keep your version up to date, you'll need to manually merge/convert any updates and changes. For popular libraries, or those that change a lot, this can be a <em>huge</em> pain</li> <li>It's very time-consuiming - aside from modifications, you'll need a good understanding on what's going on so you can make any changes without breaking functionality</li> <li>If there's multiple people working with it, they can't rely as much on external documentation/examples, as the internals might have changed</li> </ul> <p><strong>Extend the classes</strong></p> <p>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. <code>MyBox2DClass.foo()</code> could simply be a wrapper for <code>Box2DClass.bar()</code>)</p> <p><em>Pros:</em></p> <ul> <li>You implement just the classes you need, and make just the changes necessary</li> <li>Your wrapper classes can still be used in the base Box2D engine - i.e. you can pass a <code>MyBox2DClass</code> object to an internal method that takes a <code>Box2DClass</code> and you know it'll work</li> <li>It's the least amount of work, out of all three methods</li> </ul> <p><em>Cons:</em></p> <ul> <li>Again, if you plan to keep your version up to date, you'll need to check that any changes don't break your classes. Normally not much of a problem, though</li> <li>Can introduce confusion into the class, if you create your own functions that call their Box2D equivalent (e.g. "Should I use <code>setPos()</code> or <code>SetPosition()</code>?). Even if you're working on your own, when you come back to your class in 6 months, you'll have forgotten</li> <li>Your classes will lack coherence and consistency (e.g. some functions using your naming methodology (<code>setPos()</code>) while others use that of Box2D (<code>SetPosition()</code>))</li> <li>You're stuck with Box2D; you can't change physics engines without a lot of dev, depending on how your classes are used throughout the project. This might not be such a big deal if you don't plan on switching</li> </ul> <p><strong>Composition with your own classes</strong></p> <p>You make your own classes, which internally hold a Box2D property (e.g. <code>MyPhysicalClass</code> will have a property <code>b2Body</code>). You're free to implement your own interface as you wish, and only what's necessary.</p> <p><em>Pros:</em></p> <ul> <li>Your classes are cleaner and fit in nicely with your engine. Only functions that you're interested in are exposed</li> <li>You're not tied to the Box2D engine; if you want to switch to Nape, for example, you only need to modify your custom classes; the rest of your engine and games are oblivious. Other developers also don't need to learn the Box2D engine to be able to use it</li> <li>While you're there, you can even implement multiple engines, and switch between them using a toggle or interfaces. Again, the rest of your engine and games are oblivious</li> <li>Works nicely with component based engines - e.g. you can have a Box2DComponent that holds a <code>b2Body</code> property</li> </ul> <p><em>Cons:</em></p> <ul> <li>More work than just extending the classes, as you're essentially creating an intermediary layer between your engine and Box2D. Ideally, outside of your custom classes, there shouldn't be a reference to Box2D. The amount of work depends on what you need in your class</li> <li>Extra level of indirection; normally it shouldn't be a problem, as Box2D will use your Box2D properties directly, but if your engine is calling your functions a lot, it's an extra step along the way, performance wise</li> </ul> <p>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 <em>extend</em> 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.</p> <p><strong>----- Update 20/9/2013 -----</strong></p> <p>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 <a href="http://divillysausages.com/files/TestExtendClass.as" rel="nofollow">http://divillysausages.com/files/TestExtendClass.as</a></p> <p>In it, I test a number of classes:</p> <ul> <li>An <code>Empty</code> instance; a Class that just extends <code>Object</code> and implements an empty <code>getPostion()</code> function. This will be our benchmark</li> <li>A <code>b2Body</code> instance</li> <li>A <code>Box2DExtends</code> instance; a Class that extends <code>b2Body</code> and implements a function <code>getPosition()</code> that just returns <code>GetPosition()</code> (the <code>b2Body</code> function)</li> <li>A <code>Box2DExtendsOverrides</code> instance; a Class that extends <code>b2Body</code> and overrides the <code>GetPosition()</code> function (it simply returns <code>super.GetPosition()</code>)</li> <li>A <code>Box2DComposition</code> instance; a Class that has a <code>b2Body</code> property and a <code>getPosition()</code> function that returns the <code>b2Body's</code> <code>GetPosition()</code></li> <li>A <code>Box2DExtendsProperty</code> instance; a Class that extends <code>b2Body</code> and adds a new <code>Point</code> property</li> <li>A <code>Box2DCompositionProperty</code> instance; a Class that has both a <code>b2Body</code> property and a <code>Point</code> property</li> </ul> <p>All tests were done in the standalone player, FP v11.7.700.224, Windows 7, on a not-great laptop.</p> <p><strong>Test1: Size</strong></p> <p>AS3 is a bit annoying in that if you call <code>getSize()</code>, it'll give you the size of the object itself, but any internal properties that are also <code>Objects</code> will just result in a <code>4 byte</code> 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.</p> <p>Thus I turned to the <code>flash.sampler</code> package. If we sample the creation of our objects, and add up all the sizes in the <code>NewObjectSample</code> 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 <code>log</code> calls in the test file).</p> <ul> <li>Empty's size is 56 // extends Object</li> <li>b2Body's size is 568</li> <li>Box2DExtends's size is 568 // extends b2Body</li> <li>Box2DExtendsOverrides's size is 568 // extends b2Body</li> <li>Box2DComposition's size is 588 // has b2Body property</li> <li>Box2DExtendsProperty's size is 604 // extends b2Body and adds Point property</li> <li>Box2DCompositionProperty's size is 624 // has b2Body and Point properties</li> </ul> <p>These sizes are all in bytes. Some points worth noting:</p> <ul> <li>The base <code>Object</code> size is <code>40</code> bytes, so just the class and nothing else is <code>16</code> bytes.</li> <li>Adding methods doesn't increase the size of the object (they're implemented on a class basis anyway), while properties obviously do</li> <li>Just extending the class didn't add anything to it</li> <li>The extra <code>20</code> bytes for <code>Box2DComposition</code> come from <code>16</code> for the class and <code>4</code> for the pointer to the <code>b2Body</code> property</li> <li>For <code>Box2DExtendsProperty</code> etc, you have <code>16</code> for the <code>Point</code> class itself, <code>4</code> for the pointer to the <code>Point</code> property, and <code>8</code> for each of the <code>x</code> and <code>y</code> property <code>Numbers</code> = <code>36</code> bytes difference between that and <code>Box2DExtends</code></li> </ul> <p>So obviously the difference in size depends on the properties that you add, but all in all, pretty negligible.</p> <p><strong>Test 2: Creation Speed</strong></p> <p>For this, I simply used <code>getTimer()</code>, with a loop of <code>10000</code>, itself looped <code>10</code> (so 100k) times to get the average. <code>System.gc()</code> was called between each set to minimise time due to garbage collection.</p> <ul> <li>Empty's time for creation is 3.9ms (av.)</li> <li>b2Body's time for creation is 65.5ms (av.)</li> <li>Box2DExtends's time for creation is 69.9ms (av.)</li> <li>Box2DExtendsOverrides's time for creation is 68.8ms (av.)</li> <li>Box2DComposition's time for creation is 72.6ms (av.)</li> <li>Box2DExtendsProperty's time for creation is 76.5ms (av.)</li> <li>Box2DCompositionProperty's time for creation is 77.2ms (av.)</li> </ul> <p>There's not a whole pile to note here. The extending/composition classes take slightly longer, but it's like <code>0.000007ms</code> (this is the creation time for 100,000 objects), so it's not really worth considering.</p> <p><strong>Test 3: Call Speed</strong></p> <p>For this, I used <code>getTimer()</code> again, with a loop of <code>1000000</code>, itself looped <code>10</code> (so 10m) times to get the average. <code>System.gc()</code> was called between each set to minimise time due to garbage collection. All the objects had their <code>getPosition()/GetPosition()</code> functions called, to see the difference between overriding and redirecting.</p> <ul> <li>Empty's time for getPosition() is 83.4ms (av.) // empty</li> <li>b2Body's time for GetPosition() is 88.3ms (av.) // normal</li> <li>Box2DExtends's time for getPosition() is 158.7ms (av.) // getPosition() calls GetPosition()</li> <li>Box2DExtendsOverrides's time for GetPosition() is 161ms (av.) // override calls super.GetPosition()</li> <li>Box2DComposition's time for getPosition() is 160.3ms (av.) // calls this.body.GetPosition()</li> <li>Box2DExtendsProperty's time for GetPosition() is 89ms (av.) // implicit super (i.e. not overridden)</li> <li>Box2DCompositionProperty's time for getPosition() is 155.2ms (av.) // calls this.body.GetPosition()</li> </ul> <p>This one surprised me a bit, with the difference between the times being ~2x (though that's still <code>0.000007ms</code> per call). The delay seems entirely down to the class inheritence - e.g. <code>Box2DExtendsOverrides</code> simply calls <code>super.GetPosition()</code>, yet is twice as slow as <code>Box2DExtendsProperty</code>, which inherits <code>GetPosition()</code> from its base class.</p> <p>I guess it has to do with the overhead of function lookups and calling, though I took a look at the generated bytecode using <code>swfdump</code> 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)</p> <p>The bytecode for <code>var v:b2Vec2 = b2Body.GetPosition()</code> is simply:</p> <pre><code>getlocal 4 callproperty :GetPosition (0) coerce Box2D.Common.Math:b2Vec2 setlocal3 </code></pre> <p>whilst <code>var v:b2Vec2 = Box2DExtends.getPosition()</code> (<code>getPosition()</code> returns <code>GetPosition()</code>) is:</p> <pre><code>getlocal 5 callproperty :getPosition (0) coerce Box2D.Common.Math:b2Vec2 setlocal3 </code></pre> <p>For the second example, it doesn't show the call to <code>GetPosition()</code>, 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.</p> <p>Some points to keep in mind:</p> <ul> <li><code>GetPosition()</code> doesn't really do anything; it's essentially a getter disguised as a function, which is one reason why the "extra class step penalty" appears so big</li> <li>This was on a loop of 10m, which you're unlikely to doing in your game. The per-call penalty isn't really worth worrying about</li> <li>Even if you <em>do</em> worry about the penalty, remember that this is the interface between your code and Box2D; the Box2D internals will be unaffected by this, only the calls to your interface</li> </ul> <p>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.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload