Mutation listeners cause slowdown in two ways:
1) Firing the event takes time O(tree depth at which mutation
happened), with a constant that can easily be comparable to the
cost of the mutation itself.
2) Creating the event object includes various operations to grab the
information mutation event objects carry (e.g. the old and new
values for attribute changes); generating this can be expensive,
because generating the string representation of some attributes is
expensive (thing multi-dozen-kilobyte SVG path attribute, or large
inline style block), and because conversion from our internal types
to the ones mutation events want is expensive for nodes.
Some of these issues we could mitigate, of course....
Is there a simple way to test the slowdown? I'm thinking with and
without Firebug open kind of test.
Sure; just write whatever code you want to test and run it and time, no?
Is it still the case that adding a mutation listeners and removing it
again won't bring back original speed?
Yes. There are no plans to change this, fwiw.
>> Is it still the case that adding a mutation listeners and removing it
>> again won't bring back original speed?
> Yes. There are no plans to change this, fwiw.
I'm curious; what's left behind when removing the mutation listener that
causes performance impact?
The reason we don't always pay the cost of firing mutation events is
that we check whether there's a mutation listener before firing them.
This check consists of two steps:
1) Check a bit on the window that indicates that at some point a
mutation listener for the relevant event type was added to something in
2) If the bit is set, walk up the tree from the target node checking
whether the listeners are on that path.
If we find a listener as a result, we fire the event.
If you add and remove the listener, the bit remains set, so we have to
do step 2 above.
Can't that bit be replaced by a counter which is incremented when adding
a mutation listener and decremented when removing one, so that we can
only check its value against 0, and skip step 2 if it is?
Yes, at some cost in memory and time to handle a rare case. In
practice, mutation listeners, when actually used, are very rarely
removed. So the win would be purely theoretical.
Given further that many of the things that make mutation events slow to
fire are also some of the things that are completely useless with
mutation events (e.g. the old/new value stuff), we'd really rather focus
on a mutation event behavior that makes sense and deprecating existing
You can see some basic numbers that I pulled out of the Dromaeo test
suite with and without simple mutation listeners in place here:
Slightly off topic but is "DOMAttrModified" a DOM mutation listener? On
the scale of listeners how much impact does listening to this event
have? Is there a better alternative?
Philip Chee <phi...@aleytys.pc.my>, <phili...@gmail.com>
Guard us from the she-wolf and the wolf, and guard us from the thief,
oh Night, and so be good for us to pass.
> On the scale of listeners how much impact does listening to this event
It slows down all attribute changes; the amount of slowdown depends on
the exact attribute being set, the styles on the page, etc. A good
estimate is probably 2x slower, but I haven't measured recently.
> Is there a better alternative?
From JS, sadly not yet.
>Is there a better alternative?
I've never tried this, but broadcasters might be. See
Warning: May contain traces of nuts.
I could still clarify that mutation events have
large performance impact in all the browsers.
I'm curious too. When you say "there are no plans to change this", is
that purely a lack of plans (perhaps because it just doesn't matter), or
is there some reason why this is difficult to change? It seems easy
enough to clear the bit when a listener is removed and the resulting
list is empty, but perhaps there's a race condition involved? Or some
Not good. I'm porting some Firefox front end code to SeaMonkey that
makes heavy use of DOMAttrModified.
That was the first thing I thought of, but the menu items in the Firefox
code I'm trying to port are dynamically generated and don't have IDs.
Hmm but there may be a way around this. Let me think about it
If there's a more performant way to do it, I'm sure the Firefox folks
would be happy about a patch as well ;-)
Note that any statements of mine - no matter how passionate - are never
meant to be offensive but very often as food for thought or possible
arguments that we as a community needs answers to. And most of the time,
I even appreciate irony and fun! :)
> It seems easy enough to clear the bit when a listener is removed and
> the resulting list is empty, but perhaps there's a race condition
> involved? Or some other complexity?
Each element has its own list. However there is only one bit for the
entire document that says "at least one element has had a mutation event
handler". It's done this way so that if the bit is clear we don't
actually have to trawl through looking for the event handler(s).
Per Robert's reply as well - wouldn't it be possible to adapt this code so the
items have predictable, dynamically generated IDs?
Well, in Firebug, we could make the HTML panel have a enable/disable
option like the other panels, if it made a difference to people. But
if hooking things up once, causes the slowdown forever, then I guess
that is why the panel is always active. Is there a bugzilla entry for
fixing this, even if no one wants to do it? At least to track it,
should someone change their mind?
The cost of the mutation listener itself is the least of the Firebug
HTML panel's performance problems. Most of the cost is in the panel
code itself (esp. the domplate gunk), last time I profiled.
> Is there a bugzilla entry for
> fixing this, even if no one wants to do it? At least to track it,
> should someone change their mind?
Not at the moment. Feel free to file!
One other thing worth filing: a request for a better API for Firebug to
use. DOMi doesn't have this problem, for example, since it uses a C++
But the domplate only hits when adding info to the panel (first HTML
look, inspect, user hits a twisty). The mutation listener cost is
continuous, independent of Firebug-clicks, and continues after Firebug
and it's domplate are gone. This is reason I asked originally, because
it will appear to affect Firefox performance even when a user does not
get value from Firebug view.
This search suggests that we're using it only in bookmarkProperties.xul:
Instead of looking at the actual patch/bug I really should have looked at:
1. the current mozilla-central tabbrowser of the all-tabs popup.
2. the Thunderbird tabmail implementation.
3. the MailNews tabmail implementation.
Firefox solved this by applying a sledgehammer to the problem.
The Thunderbird tabmail implementation appears to be frozen in time and
still uses DOMAttrModified :S
In the SeaMonkey tabmail implementation Mnyromyr found a much more
elegant solution which we should all steal.
I think my brain shorted out trying to reconcile three^Wfour
divergent tabbrowser implementations. Can we please unfork at least some
of the code into a generic toolkit base binding, pretty please?
 Damn ingenious these Germans.
 Firefox tabbrowser, SeaMonkey tabbrowser, Thunderbird tabmail,
 Gawd knows what sort of evil mutant tabbrowsers Spicebird,
Instantbird, and Postbox are using.
If someone does file a bug regarding that, please mark bug 578218 as
dependent, or let me know about it.
You know, a really crazy thought occurred to me some months ago, which
is that someone could write a shim that implements nsITreeBoxObject and
either rests between inDOMView and its box object, or replaces its box
object completely. When the fake box object captures calls to
RowCountChanged, it means a node was inserted, a node was removed, or a
twisty was clicked to show its children (or an attribute was added or
removed, if whatToShow allows them to be shown).
But yeah, don't take this as an encouragement to actually do that.
Having mutation event listeners affects only to the performance of the
document/window in which they are used.