Wednesday, February 18th, 2009

JavaScript Inheritance Performance

Category: JavaScript, Performance

Robert Kieffer has run some benchmarks on the performance of JavaScript inheritance using different inheritance techniques:

One area of the Prototype JavaScript library that I have a bit of a love-hate relationship with is its support for mimicing OO inheritance.   The ability to call $super from within a function to refer to a superclass’ implementation is pretty darn cool, but I’ve I’ve found myself more and more annoyed at having to navigate what seem like unnecessarily deep and complicated stack traces when trying to debug code.  It was while mired in the bowels of Class.create() and Class#addMethods() that I found myself wondering how much of a performance penalty this stuff was incurring.  To find out, I put together a test to determine how different the strategies for emulating OO inheritance in JavaScript performed.  Here’s what I tested:

  • Ad hoc inheritance – This is a common(?) homebrew technique for allowing prototypes to leverage the code in objects further up the prototype-food chain.  Methods are overridden by keeping a reference to the parent method in a separate property , which can then be invoked as needed.  It’s fast but not very pretty, and it’s arguable whether or not this qualifies as real “OO” inheritance.
  • Prototype-style inheritance – Prototype uses a strategy inspired by Alex Arenell’s Inheritance library. Subclass methods declare a “$super” argument that is set up by Prototype to reference the superclass’ method.
  • Base2-style inheritance – Dean Edwards’ library.  Subclass methods invoke “this.base()” to call their superclass’ implementation.
  • John Resig inheritance – JR, of jquery fame, experimented with a Base2 variant which he published on his blog.  It’s a bit simpler than Base2, but seemed worth testing.

Tests were performed by using  JSLitmus to create a Inheritance Performance testbed.

Robert shows that when $super is used, you get a perf drop with Prototype, however he also realizes that this is “only really important if you’re making 100,000’s of calls per second to overridden methods, however.”

Posted by Dion Almaer at 6:34 am
17 Comments

++++-
4.1 rating from 14 votes

17 Comments »

Comments feed TrackBack URI

Isn’t it refreshing to see people taking an interest in something other then selector engines (for speed boosts), for a change?

Comment by sixtyseconds — February 18, 2009

@sixtyseconds
Yes, definitely. The problem though isn’t about selector engines versus OO speed and such. It runs much deeper unfortunately where it’s a fact that 99% of all the energy spent on Ajax today is being spent on the wrong things. 15 years from now none of us will hardly remember the stuff that hits the main stream media today… :(
.
I guess we can pretty safely conclude with; “so much for building pyramids”…

Comment by ThomasHansen — February 18, 2009

Benchmarks are not valid, because object constructors should also be overidden

Comment by Covex — February 18, 2009

There is something wrong with JSLitmus and last executed test which for some reason is always slower than others (I simply moved a couple of tests from top to end and vice-versa, results are systematically slower if placed at the bottom).

Comment by WebReflection — February 18, 2009

@Covex – Can you elaborate on why you think these benchmarks are invalid? I’m not sure what you mean when you say “object constructors should also be overridden”.

Comment by broofa — February 18, 2009

@WebReflection: Can you please post the test you’re using that shows inconsistent performance? I put together a simple consistency test ( http://broofa.com/Tools/JSLitmus/tests/consistency.html ) and don’t see any issues. Here are my results:

Note: I’m not saying JSLitmus provides 100% accurate results, btw. Results will be affected by the browser doing garbage collection, by other apps taking CPU time, and even by OSes that vary CPU speed in response to app demand (typically only seen on laptops.) But I haven’t seen the particular issue you’re referring to.

Comment by broofa — February 18, 2009

whups, results here: http://tinyurl.com/bmjwae

Comment by broofa — February 18, 2009

OO and class-based don’t mean the same thing. javascript developer’s shouldn’t try to mimic classes. classes are a limitation that javascript doesn’t have.

Comment by robertlovescss — February 18, 2009

@ robertlovecss

What are you referring to with the “shouldn’t try to mimic classes” line?

I use MooTools, and since I started writing code in the class format they offer that is typical in other languages, my code is waaay more portable, very DRY, reusable, extensible, and clean. That is the biggest win I got from using the framework thus far. Classes used in this way give you an edge in my opinion.

Comment by csuwldcat — February 18, 2009

@ broofa: I meen, that all classes should contain initialization methods (“init” for John Resig’s solution, “constructor” for Base2 and “initialize” for Prototype.js). And there should be a call to initialization method of parent class from childClass’s initsializator.

Comment by Covex — February 18, 2009

@Covex: Ah, gotcha. I didn’t see a need to override the constructor functions in this set of tests. You can extrapolate what that performance hit will be based on the performance of the calls to the subclass method. The mechanics of overriding a constructor are no different from that of any other method. This way, you can get a feel for what the overhead of the constructor process itself is, independent of the method override penalty.

Note, too, that object instantiation is typically a relatively infrequent operation compared to method invocation. In most cases a performance hit of incurred when creating an object will be much less than the hit you take in subsequent method calls on the object.

Comment by broofa — February 19, 2009

Hi broofa,

I liked your article, however, using John Resig’s Class is a bit unfair. It doesn’t do half the stuff that mootools and prototype do and it’s broken/not complete. In IE it will break if you use a method name that mirrors those with the DontEnum attribute (mootools has the same error). Also it doesn’t return the correct constructor. Both simple fixes, but I bet folk grab that code as it appears to be the fastest and then will run in to problems.

I’ve uploaded another test similar to yours that also tests for the above problems, letting you know if they “Passed” or “Failed”. I’ve also included in the tests my own Class implementation which I’ve been using for a while now and it seems to perform the best, this is due to the way I do parent method calling.

http://www.projectcss.net/oop-test/

Comment by atwork8 — February 19, 2009

@atwork8: your code works incorrect.
If you will remove j3_Level2::foobar function, then j3_Level1::foobar function will be called twice (try this to check out: var Obj = new j3_Level4(1, 2); Obj.foobar();)

Comment by Covex — February 19, 2009

@WebReflection, your code does not work as expected =(

function WR_Level0() { }
function WR_Level1() { this.parent(); }
wr.extend(WR_Level1, WR_Level0, { });
function WR_Level2() { this.parent(); }
wr.extend(WR_Level2, WR_Level1, { });
var Obj = new WR_Level2(); // too much recursion

Comment by Covex — February 20, 2009

Covex, thanks to your test I realized I forgot the parent chain and I fixed it instantly :D
Why don’t you try the last version and these classes?

var wr_Level0 = wr.extend(
function(name, age){
this.name = name;
this.age = age;
}, {
foobar: function() {
return “fubar”;
}
});
var wr_Level1 = wr.extend(
function(name, age){
this.parent(name, age);
}, wr_Level0, {
foobar:function(){
return this.parent();
}
});
// up to level4

In my test my method wins over every other in that scenario ;-)

Comment by WebReflection — February 20, 2009

@atwork8, your solution seems to be “unreasonably fast” with Internet Explorer but with new engines it is not that fast. At the same time the trick over toString is not elegant and could fail with the first library that override toString function in the Function.prototype, isn’t it?
Maybe we could combine both solutions to find the fastest trick maintaining code elegance, can we?

Comment by WebReflection — February 20, 2009

@Covex – Thanks for pointing out the error with my code. I had a chance to look at it today and corrected the problem. It should now be working as expected. I’ve posted it up using your test and it still appears to be the fastest at parent method calling:

http://www.projectcss.net/oop-test/2/

As I said above I’ve been using this for a while with no problems, the reason being, I don’t use parent method calling all that often lol Makes me wonder how useful it actually is? I mean, if parent methods aren’t being called all the time, then it only takes a second to type an explicit call.

@WebReflection – I’m not sure what you mean by “unreasonably fast”? In the context is that an oxymoron? lol

“the trick over toString is not elegant and could fail with the first library that override toString function”

My class implementation is totally modular, relying on standalone OO methods to do the work, so if somebody wanted a more thorough function for copying over methods they can feel free to swap out the one provided. I haven’t run in to any problems with the current implementation and I’ve been using it for a while. I like to keep things as complicated as necessary :o)

“Maybe we could combine both solutions to find the fastest trick maintaining code elegance, can we?”

Not sure if that was aimed at me, but feel free to drop me an email. My address is my username – @hotmail.co.uk

Thanks

Comment by atwork8 — February 25, 2009

Leave a comment

You must be logged in to post a comment.