Hi all, I've come up with a new way of handling $super (which I can't imagine is unique, it's just new to me) which is markedly more efficient than our current $super on all of the browsers I've tried it on. I'm wondering whether we should consider it for Prototype 2.
Executive summary: Pros - * Avoids calling toString on subclass methods when defining classes. * Avoids creating closures for each $super-enabled subclass method. * Avoids creating an on-the-fly function on every call to a $super- enabled method. * Avoids at least four extra calls when $super-enabled methods are called. * Consequently reduces the time to call methods that use $super, from markedly (1.6X as fast) to dramatically (10X as fast) depending on browser and choices the subclass author makes. * Theoretically reduces memory footprint (no added functions/ closures). * Minifiers (like packer3) that change arg names don't break things by renaming $super. * (Subjective) Simpler code in Class.create for contribs to understand. Cons - * Breaks backward compatibility. * Syntax for calling the $super method is slightly more complex. Details: A brief review of how $super currently works: 1. When a class is defined, Class.create calls Function#argumentNames on each method defined by the subclass. (#argumentNames is Function#toString followed by three regexes and a String#split.) Class.create then checks the first argument to see if its name is "$super" and, if so, creates a closure to hold the ancestor and wraps the member function to set up the $super goodness. 2. When a $super-enabled method is called, it is in fact our wrapper that is called. The wrapper creates a new function on the fly (on every call) to bind our closure to "this" and passes the result to the subclass's real method as the $super parameter. This results in the wonderfully simple syntax supported by Prototype classes, that you can call the super's version of a function simply by calling $super: nifty: function($super, foo, bar) { $super(foo); } You don't even have to pass in "this", it's all handled for you, though at a substantial runtime cost. (Note that in this example, only 'foo' and not 'bar' is passed to the super; presumably 'bar' has meaning only to the subclass.) The new approach is markedly simpler and more direct at both the class definition stage and the method call stage. However, calling the $super version of the function is not quite as straightforward. Here's how the new approach works: 1. At class definition time, Class.create detects that the subclass overrides the superclass's method (this is a trivial property check) and, if so, stores a reference to the super's function as a property called $super on the sub's function object. 2. When a method is called, there is no indirection, the call goes direct to the subclass's defined method. The subclass can then call the super's version via arguments.callee.$super. The unaided syntax subclass methods would use to call the super's version is complicated and error-prone (to the novice, anyway): nifty: function(foo, bar) { arguments.callee.$super.call(this, foo); } ...and so this solution envisions a helper "callSuper" method (name TBD) authors may choose to use instead (at the cost of an extra function call): nifty: function(foo, bar) { this.callSuper(arguments, foo); } You can see why I think a helper may be useful; that's quite a lot simpler. Note that the first argument is always 'arguments' and does *not* mean #callSuper is passing all of the arguments to the super function; you follow it with the arguments you want to pass. We can muck about with the API on the helper, and of course class authors can create their own for their own needs. In my tests on Windows, IE shows the least improvement and Chrome the most. Specifically, my simple tests so far (all of these numbers are rounded, and they vary quite a lot from test to test, so these are just indicative): Using callSuper syntax: * IE: 38% reduction in method call time, e.g., calls are 1.6 times as fast * Safari: 58% reduction; 2.4 times as fast * Firefox: 65% reduction; 2.9 times as fast * Opera: 66% reduction; 2.9 times as fast * Chrome: 83% reduction, calls are 5.8 times as fast Using direct (but complicated) syntax: * IE: 40% reduction, 1.7 times as fast * Safari: 77% reduction; 4.4 times as fast * Opera: 77% reduction; 4.4 times as fast * Firefox: 80% reduction; 4.9 times as fast * Chrome: 91% reduction, calls are 10.1 times as fast Using the complicated syntax, that's a full order of magnitude *or better* on Chrome (but let's face it, Chrome is already so much faster than everything else it's less important than it would otherwise be), and Firefox sees ~5X improvement. With the syntax I would expect most class authors to use, the benefit is less but still marked. Call overhead isn't sexy, but does anyone else think this is worth looking at more closely? -- T.J. Crowder tj / crowder software / com www.crowdersoftware.com --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Prototype: Core" group. To post to this group, send email to prototype-core@googlegroups.com To unsubscribe from this group, send email to prototype-core-unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/prototype-core?hl=en -~----------~----~----~----~------~----~------~--~---