Open Bug 543494 Opened 11 years ago Updated 9 years ago
Very slow performance for textarea
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2) Gecko/20100115 Firefox/3.6 Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2) Gecko/20100115 Firefox/3.6 I have a page with 1770 textarea elements. The page takes around 60000ms (1 minute) to load. I turned off the spellchecker which increases the speed very much. But it still takes 16000ms to load the page. I've compared that to IE 8 (700ms) and Chrome 4 (200ms). Using input elements reduces the time beeing used down to 3000ms. Using just div-Elements takes around 300ms. This bug might be related to bug 335984 where they asked to file separate bugs. Reproducible: Always
Boris, bug 221820 can't solve this, since it only makes text and password input fields initialize lazily. I filed bug 543552 to track porting the same work over to textareas.
Seems to be only an issue when reflows are occurring (in visible mode.) Will investigate more tomorrow.
OK. So we spend something like 15% of the total time initializing editors (in the 1000-textarea case). On the other hand, about 82% of the time is spent under reflow, and almost all of this is under nsGfxScrollFrameInner::FinishReflowForScrollbar. The remaining percentages are all percentages of time under ProcessReflowCommands: 30% under a SetAttr call from FinishReflowFromScrollbar directly. This is almost entirely under nsXBLBinding::AttributeChanged, which does more attr sets and notifications, complete with range addref/release all over in nsNodeUtils, etc. 68% under nsGfxScrollFrameInner::SetCoordAttribute, calling SetAttr, calling nsXBLBinding::AttributeChanged, which does more attr sets, etc. For a bottom-up view, 18% is in nsCOMPtr_base::assign_with_AddRef, 12% is in cycle collector suspect, 10% is in cycle collector forget, 10% is in (not under!) nsRange::AddRef, 8% is in non-virtual thunk to nsRange::AddRef, 7% is in nsRange::Release, 7% is in (not under) nsNodeUtils::AttributeWillChange, 7% in (no under) nsNodeUtils::AttributeChanged, 6% in non-virtual thunk to nsPlaintextEditor::AddRef, 6% in (not under) nsEditor::AddRef, 3% in nsEditor::Release, 2% in non-virtual thunk to nsRange::Release. Which raises an obvious question: what attributes are being changed by scrollbar code, what do the scrollbar bindings do with those, and why are all these ranges involved? Are these the selection ranges for the textareas getting notified for the scrollbar attr changes because they're observers on an ancestor of the scrollbar? I assume (or at least hope!) those ranges are not observers on the document, right?
I don't know. A lot of this would probably go away if we made scrollbars be a single custom object instead of a complex XBL binding.
Ranges do observe the root of the subtree they are in, which is most often the document. They essentially have to since they need to be notified when any ancestor of an endpoint is moved.
The lazy editor init would help some in terms of not needing us to have selection ranges or editors observing the attr changes... I just checked, and the node that's the |node| in IMPL_MUTATION_NOTIFICATION when we call nsRange::AddRef is the nsHTMLTextAreaElement.
Er, no. Sometimes it's the nsHTMLTextAreaElement. But most of them it seems to be the document. Which means that for each of those attr sets we're notifying the selection range of every single textarea. That explains the slowness, to a large degree.
Oh,and all the editors seem to also observe the document. That's ridiculous: they should be observing the anonymous div, imo.
So bz pointed out that when it comes to anon content, ranges behave differently. Looking at the code it looks like ranges that are inside anon content observe the binding parent. However, selection ranges are an exception, and they always observe the root of the subtree they are in, i.e. generally the document.
(In reply to comment #10) > Oh,and all the editors seem to also observe the document. That's ridiculous: > they should be observing the anonymous div, imo. I _think_ that's because of the fact that the root of the HTML editor can be the document root. I have no idea why HTML editor's root works like that, though. I would expect it to be the contenteditable element itself.
OK, so there are tons of inline spell-checker ranges floating around here. At least some of them have the textareas as roots, because they're set up with the anon div as start and end parent. So they end up seeing all the scrollbar attr changes. :(
Ehsan, there's no HTML editor here. Just a plaintext editor, and its root is most emphatically the anonymous div.
(In reply to comment #14) > Ehsan, there's no HTML editor here. Just a plaintext editor, and its root is > most emphatically the anonymous div. Yes, but the nsEditor code is shared with HTML editors as well, so it has to observe the mutation events on the document, not the anonymous div. In fact, the nsEditor code does not know anything about the anonymous div.
If I disable spellcheck, the testcase speeds up by a factor of 3. And I stop seeing ranges coming through that have the document as their root. I also never see any selection ranges inited in this testcase... So it sounds like spellcheck is leaving ranges hanging around that are rooted at the document for some reason. Or something.
> so it has to observe the mutation events on the document, not the anonymous div. Does it? It seems to only care about cases when its root is removed from the document (though there's the weird insert/append code that makes no sense), and it could get that by observing its root only. > In fact, the nsEditor code does not know anything about the anonymous div. Perhaps it should (most simply, if it's got a root passed to Init() and the root is native anon, observe it and not the document).
If I disable spellcheck _and_ make editor observe its root when given an explicit root, then the time for empty textareas in Ehsan's benchmark goes from 17000ms to 850ms. It's about 5500ms with just the spellcheck disabled. That's still a good bit slower than Chrome, but is a heck of a lot closer. Sounds like we should get bugs blocking this one filed on those two issues, at least.
Oh, and that change to observe the root does make us crash when closing the page, so clearly needs a bit of tweaking (observe the root's parent? But then we'll get notified for all those sibling textareas...)
Oh, and in case someone cares there are 4 SetCoordAttribute calls under FinishReflowForScrollbar. The sets for curpos and maxpos trigger 5 setattr calls each in the binding. The sets for pageincrement and increment only trigger one setattr call each. Times two scrollbars, of course....
I haven't been able to tell yet where the ranges which have the document as mRoot and are set up with the body as start and end parent come from... Something in spellcheck produces them, as far as I can tell, but I can't tell what.
Some data with and without lazy initialization: Lazy initialization: 15199, 15548, 15307, 15380, 15302. Avg: 15347.2 Eager initialization: 14840, 15108, 15310, 15039, 15053. Avg; 15070 So, I don't think lazy editor initialization is doing much help here (which is weird).
(In reply to comment #17) > > so it has to observe the mutation events on the document, not the anonymous div. > > Does it? It seems to only care about cases when its root is removed from the > document (though there's the weird insert/append code that makes no sense), and > it could get that by observing its root only. The content removal observer is straightforward, but I can't figure out the insert/append code as well. > > In fact, the nsEditor code does not know anything about the anonymous div. > > Perhaps it should (most simply, if it's got a root passed to Init() and the > root is native anon, observe it and not the document). Well, for plaintext editors, the root is always passed via Init. But for HTML editors, the root is not passed, and is dynamically set when the first call to GetRootElement is made (which makes little sense anyway.) But yes, I agree that the root should not be something like the body or the document root, and it's in my todo list to fix it.
Ah, nsTextServicesDocument::InitWithEditor seems to call CreateDocumentContentIterator which sets up a range on the body.... and then leaves it around. :( This is called from nsEditorSpellCheck::InitSpellChecker.
Comment 22 really surprises me, since in that case there should be no editors or ranges around (e.g. no spellcheck). What's going on there?
(In reply to comment #25) > Comment 22 really surprises me, since in that case there should be no editors > or ranges around (e.g. no spellcheck). What's going on there? Well, textareas are _not_ lazily initialized yet. :-) I thought that maybe you expected a performance win even for textareas for some reason, so I just wanted to verify that assumption. BTW, the lazy init definitely helps with normal inputs: Empty input boxes, eagerly inited: ~250; lazily inited: ~150 (~40% win). Non-empty input boxes, eagerly inited: ~450; lazily inited: ~165 (~60% win).
Oh, ok. No, I wouldn't expect a win from the non-textarea lazy init. Textarea lazy init should help a good bit on your test case, though. No idea whether that's indicative of what it'll do on Daniel's test page. I'll try to file some bugs tonight blocking this one on the editor/range observer stuff.
Filed bug 544503 on editor observing the document. Filed bug 544504 on spellcheck setting up whole-document ranges.
Some Benchmark-Tests with different Firefox versions: Results - Nightly version 6028 - (2004-12-31) 2258 - (2005-12-30) 2705 - (2006-02-28) 5663 - (2006-05-31) 4564 - (2006-12-31) 3337 - (2007-12-31) 3565 - (2008-12-31) 4429 - (2009-05-31) 6013 - (2009-09-30) 5785 - (2009-11-30) 5599 - (2010-02-02) I've always tested the "Non-empty textarea" benchmark with "visible" checked and 1000 elements. I've not changed anything of the default settings. Just downloaded the version and started it. Interesting to see that the version from 2005-12-30 was 2.5 times faster compared to the current version (spell checking disabled).
That would be because bug 544503 was not an issue before sometime in 2007, for one thing. In any case, the right thing to do is fix bug 544503 and bug 544504 and bug 543552 and then reprofile.
OK, I see. Just for the records some more results I've already tested: 4352 - (2009-07-31) 4361 - (2009-08-12) 22.214.171.124pre 5761 - (2009-08-13) 1.9.2a2pre 5598 - (2009-08-15) 5574 - (2009-08-31)
You need to log in before you can comment on or make changes to this bug.