Open Bug 520621 Opened 16 years ago Updated 3 years ago

XPath is much slower than Safari

Categories

(Core :: DOM: Core & HTML, defect, P5)

x86
macOS
defect

Tracking

()

People

(Reporter: jrmuizel, Unassigned)

References

(Blocks 1 open bug, )

Details

(Keywords: perf)

Attachments

(1 file, 1 obsolete file)

Ikea uses some XPath in a mouse over handler via an older version of prototype. When you scroll the page this handler gets called and since it takes so long, scrolling becomes very slow. The linked to URL has a version of this page along with a "Time" button that performs the operation. In Safari/Chrome the operation takes about 16ms, in Firefox we take about 120ms which is long enough to cause a noticeable pause during scrolling.
Based on a Shark profile, most of the time is spent in XPath engine.
I should also mention a couple more things: First, the jist of what is slow here is (doSelectTimeTest): $$('a[rel]').each(function(element) { var img = element.down("img"); count += 1; }); In particular, the element.down() call is the one that takes 100+ ms Second, Opera and IE are also not very good at this case.
There are several problems. - txStack is inefficient, because pop is used all the time so that the stack gets empty and that causes free calls, and next push will malloc. - Recycler truncates strings, which will then get some value; way too much freeing/mallocing. Might be able to save some time if values were just assigned without truncating the old value first. - Would it be possible to make LocationStep::fromDescendants iterative, not recursive? That might help in this case a bit.
I believe bug 474369 made this significantly worse. nsVoidArray does less mallocing/freeing when used the way txStack does.
Blocks: 474369
(In reply to comment #3) > There are several problems. > - txStack is inefficient, because pop is used all the time so that the stack > gets empty and that causes free calls, and next push will malloc. Yup, should do that. > - Recycler truncates strings, which will then get some value; > way too much freeing/mallocing. Might be able to save some time if > values were just assigned without truncating the old value first. Yup, should do that. > - Would it be possible to make LocationStep::fromDescendants iterative, not > recursive? That might help in this case a bit. Why would that be more performant? I do bet we can avoid a fair number of virtual calls while iterating over the tree though by making txXPathTreeWalker use nsINode::ChildIterator
At least the patch reduces freeing/mallocing quite a bit.
(In reply to comment #4) > I believe bug 474369 made this significantly worse. > nsVoidArray does less mallocing/freeing when used the way txStack does. I filed bug 520671 on this
Would it make sense to measure typical stack sizes and use nsAutoTArray?
That probably would be a good idea, if I recall correctly we'll generally place fairly few objects on these stacks. Though I really think we need to fix nsTArray as well.
Changing nsTArray's behaviour is a tough call. If an array stays empty for a long time, then you want to have freed the buffer, otherwise you don't. nsTArray can't know this ahead of time on its own. We could use nsExpirationTracker or something to free buffers after a delay but maybe that's overkill.
For fun, I ported the code to use querySelectorAll() and while we are much faster we still get beaten by Safari a fair amount: http://people.mozilla.com/~jmuizelaar/p/xpath/query-selector/10609.html Firefox: 43ms Safari: 12ms
Attached patch nsAutoTArraySplinter Review
naAutoTArray helps here
Attachment #404721 - Attachment is obsolete: true
Attachment #404791 - Attachment description: naAutoTArray → nsAutoTArray
Attachment #404791 - Flags: review?(jonas)
Comment on attachment 404791 [details] [diff] [review] nsAutoTArray >diff --git a/content/xslt/src/base/txStack.h b/content/xslt/src/base/txStack.h >--- a/content/xslt/src/base/txStack.h >+++ b/content/xslt/src/base/txStack.h >@@ -36,17 +36,17 @@ > * > * ***** END LICENSE BLOCK ***** */ > > #ifndef txStack_h___ > #define txStack_h___ > > #include "nsTArray.h" > >-class txStack : private nsTArray<void*> >+class txStack : private nsAutoTArray<void*, 8> This definitely makes it less likely that the badness will happen, but I'd really like to fix nsTArray. But that's bug 520671. >diff --git a/content/xslt/src/xpath/txCoreFunctionCall.cpp b/content/xslt/src/xpath/txCoreFunctionCall.cpp >- StringResult* strRes = nsnull; >- rv = aContext->recycler()->getStringResult(&strRes); >- NS_ENSURE_SUCCESS(rv, rv); >- >- *aResult = strRes; >- txXPathNodeUtils::getLocalName(node, strRes->mValue); >- >- return NS_OK; >+ nsAutoString localName; >+ txXPathNodeUtils::getLocalName(node, localName); >+ return aContext->recycler()->getStringResult(localName, >+ aResult); This isn't very optimal either since we'll end up having to copy the data in the string. Is there no way to empty without making it drop its buffer? Could we add a function to nsTSubstring?
Blocks: 522967
> Is there no way to empty without making it drop its buffer? Not for a non-auto string. We should consider adding something, especially if we're sure we'll free the string expeditiously (say before evaluate() returns).
Jeff, I can't tell exactly what the times in comment 12 are measuring; which of the js files linked from that page did you modify to make those measurements?
Keywords: perf
(In reply to comment #16) > Jeff, I can't tell exactly what the times in comment 12 are measuring; which of > the js files linked from that page did you modify to make those measurements? The times are from running the following js added to 10609.html: function doSelectTimeTest() { var start = Date.now(); var count = 0; $$('a[rel]').each(function(element) { var img = element.down("img"); count += 1; }); var end = Date.now(); alert("selecting " + count + " took " + (end - start) + "ms"); } To port prototype to querySelectorAll I made the following change: findElements: function(root) { root = root || document; + var e = this.expression, results; + if (root != document) { + var oldId = root.id, id = $(root).identify(); + id = id.replace(/([\.:])/g, "\\$1"); + e = "#" + id + " " + e; + } + results = $A(root.querySelectorAll(e)).map(Element.extend); + root.id = oldId; + + return results; + + if (this.xpath) return document._getElementsByXPath(this.xpath, root); return this.matcher(root); },
Ah, I see. I'd missed the "test" button. Just profiled that; the querySelectorAll call only takes up 30% of the time there. Want to file a jseng bug on the rest?
Comment on attachment 404791 [details] [diff] [review] nsAutoTArray I think the nsAutoTArray change here is ok, but I don't like the string changes. We should fix the string API to allow clearing a string without freeing its buffer.
Attachment #404791 - Flags: review?(jonas) → review-
https://bugzilla.mozilla.org/show_bug.cgi?id=1472046 Move all DOM bugs that haven’t been updated in more than 3 years and has no one currently assigned to P5. If you have questions, please contact :mdaly.
Priority: -- → P5
Component: DOM → DOM: Core & HTML
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: