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
-~----------~----~----~----~------~----~------~--~---

Reply via email to