Closed Bug 600554 Opened 14 years ago Closed 7 years ago

optimize querySelector(All) for cases like querying a class name or a tag name

Categories

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

defect
Not set
normal

Tracking

()

RESOLVED FIXED

People

(Reporter: ehsan.akhgari, Unassigned)

References

Details

(Keywords: perf)

We can probably special case the querySelector API in the simple cases where one passes an ID, a class name, or a tagname to it.

this was suggested in http://www.glazman.org/weblog/dotclear/index.php?post/2010/09/29/Why-is-getElementsByTagName-faster-that-querySelectorAll.
Component: DOM → DOM: Core & HTML
Keywords: perf
Note that Webkit has such an optimization for a single ID for cases when there's only one element with that ID in the document.  That'd be reasonably simple to add, but are there actual non-microbenchmark use cases?  

Also, are there any numbers on what the speedup looks like for, say, tagnames?  (Note that I have local patches that make querySelectorAll about 2x faster, btw; they just have a few kinks to work out).
(In reply to comment #1)
> Note that Webkit has such an optimization for a single ID for cases when
> there's only one element with that ID in the document.  That'd be reasonably
> simple to add, but are there actual non-microbenchmark use cases?  

Yes, I'd expect so.  If libraries such as jQuery use querySelector as their implementation for style queries, this might have real world performance improvement results.
That's an if, though.  Last I checked, jquery parsed the selector itself, pulled bits of it apart, passed some of it through to getElementById, some to querySelector, and some to its internal selector implemenation.....  Has that changed?
That said, I'm still interested in the numbers; if there's a testcase I could even test on it with the changes I mention in comment 1.
Here's a test on JSPerf:
http://jsperf.com/getelementsbytagname-a-0-vs-queryselector-a

Note that getElementsByTagName("a")[0] is much faster that querySelector("a"). I'm guessing this is likely due to caching of the NodeList results, but would be nice if the results were closer. :)
Thanks for writing that test!  A few things:

1)  document.getElementsByTagName("a")[0] and document.querySelector("a") can
    return different nodes; they're not equivalent calls.  So comparing them to
    each other is a little weird.  That said, the behavior difference is
    actually likely to make querySelector a tad faster if all else were equal. 
    What you really want to compare to, if you're comparing querySelector("a"),
    is getElementsByTagNameNS("*", "a").  I created a revision 2 of the test
    that does that, but not sure how to publish it.
2)  The nodelist caching is in fact the most likely thing that's helping here;
    this test is more or less bogus in that regard (should be using different
    tag names on every iteration if it wants a useful comparison).  I didn't
    write a test for this, because doing it right (e.g. without Math.random()
    or string concatenation dominating the time so that the test becomes
    useless) involves understanding the test harness guts sufficiently to know
    how it calibrates its loops.
3)  It would be interesting to see how much of the speed difference is due to
    the fact that querySelector has to parse the selector.  You could test that
    by calling it on a node that has no kids, possibly.  Or profiling.  Hard to
    tell.  Note that any sort of fast-path would need to either parse the
    selector or have a parsed-selector cache; we could do the latter without
    creating the fast-paths this bug suggests.
3)  The numbers I get for the querySelector part of this test are incredibly
    noisy (factor of 2 noise in them).

4)  "Usually" the numbers I get on trunk are: 1,354,050 and 427,781 

4)  With a build that has the patch I mention in comment 1, I "usually" get:

      1,569,725  and 595,782 

    I have no idea why that first number changed in the process.

5)  Is there a way I can make this harness let me run a snippet of code right
    before starting the testing loop for one of these tests and again right
    after the loop ends?  If I could do that, I could answer the question from
    item 2, say.
The site itself isn't setup for a lot of optimization, though I'm sure if you grab the source of the page you can get it to run in any way you'd like. 

Probably not the place to ask, but I am curious as to why getElementsByTagName("a")[0] could return a different node than querySelector("a").
> though I'm sure if you grab the source of the page you can get it to run in any
> way you'd like

Well, sure.  It's all software.  I was sort of hoping for a simple enough way that I could justify spending the time on it in addition to blockers.... ;)

For the other, getElementsByTagName matches on the qualified name, while querySelector matches on the local name.  So any time there's a non-null namespace prefix they'll do different things.  Though there's talk of changing this getElementsByTagName behavior, note.
We added a fast-path for the id case in bug 696205, for what it's worth.
Summary: optimize querySelector(All) for cases like querying an ID, a class name or a tag name → optimize querySelector(All) for cases like querying a class name or a tag name
Depends on: 1410624
Bug 1410624 did this.
Status: NEW → RESOLVED
Closed: 7 years ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.