A little more fastness, please? Maybe?

Over the last few years, there’s been a big movement on making Mozilla code really, really fast.  Which is wonderful.  A fair bit of this has been in “quick stubs” – specifically, optimizing how JavaScript (aka JS) code calls into C++ code.  For instance, the HTML input element has a quick stub for the value property, bypassing much of the XPConnect gobbledygook.

This post is about the reverse:  frequent calls from C++ code to JS code.  Calls like that don’t have the direct path.  They go through XPConnect.  This is understandable – JS code can do pretty much anything under the sun.  Plus, there aren’t many areas where C++ calls on JS.  The most notable spot is DOM event listeners, but again, they can do anything.

On the other hand, there are specific interfaces where the JS author is supposed to do only very simple things – or where the first few actions taken are validation (“does this event have a keyCode I care about?”).  The NodeFilter interface from DOM Traversal comes to mind.  It’s supposed to check a node’s properties and return a simple value.  A NodeFilter you pass into a TreeWalker or NodeIterator will be called for any node of the specified node types.

This is the kind of thing I look at and think, “Why don’t we get XPConnect out of the way, by having native code do the repetitive work?”  JavaScript is a glue language, where you can connect components together.  So we could use JavaScript to assemble a C++-based NodeFilter, which then gets passed into the native TreeWalker.  The purpose of this “intermediate filter” is simply to call on the JS code less frequently.

Now, I admit, node filters aren’t the most compelling case for pre-assembly.  (I have my reasons for wanting that, but they’re not common cases on the Web, I suspect.)  An event listener, though, would be more compelling.  I’d wager a native ProgressEvent listener, filtering on the readyState property before possibly executing a script-based callback, would be nice.  So would a native keypress event listener, based on keystrokes to a form control.

There’s even a bit of precedent in the Mozilla Firefox code:  the “browser status filter”.  I admit this poorly-documented web progress listener has caused me pain in the past, as it filters quite a lot… but that’s the point:  it hides a lot of unnecessary cruft from JavaScript code that frankly doesn’t care!  I also think JavaScript typed arrays are a point of comparison.

I think a few intermediate filter classes like this could have a measurable impact (ok, maybe small) on frequent calls from C++ to JS.  I don’t know if they would… but I think in the right places they could.  The major downside I can see is to readability of the code.

Opinions welcome!

5 thoughts on “A little more fastness, please? Maybe?”

  1. JS is a “glue language”? There’s your problem right there. So not true.

    Also, JS-to-JS calls avoid XPConnect. Just sayin.

  2. ( just a random planet-reader: )

    It seems to me that the case of NodeFilter could be solved if/when more code is translated from C++ to JS. In this case, if the code that loops over the tree and invokes the NodeFilter was itself written en JS, it would be an JS-to-JS-call and could be “whole-program-optimzed” by the JIT.

    The case of event listeners I guess is less of a problem, as those probably occur at most in the order of tens of times per second, where as JS-to-C++ call occur millions of times per second.

    Could you hint at why it influences the speed of the JS-to-JS call, if the JS needs to be callable from C++? C++ need to use a special “calling conversion”, but why does JS need to use the same?

    1. Well, yes and no. If your tree walker and your node filter are both implemented in JS, then they can talk to each other directly… but then your tree walker has to look up each node’s first child, next sibling, and parent node properties (under different circumstances). So you’re back to crossing between languages again – this time with the fast path, but still XPConnect. The TreeWalker was introduced long before this fast path was available.

      The best way to beat that is to write your entire Document Object Model in JavaScript. I mean the whole thing. Some very smart people are experimenting to do just that.

  3. Exactly. It will replace (presumably) slow C++-to-JS calls with fast JS-to-C++ calls and those can be removed by translating more of the C++ to JS (and people are working on that). So what did you mean by “yes *and no*”? 🙂

Comments are closed.