4 ms·
The global scope polluter has pretty bad performance and interop surprises, you shouldn't depend on it and instead use getElementById even if it's a bit more ve
by esprehn 4y ago
The global scope polluter has pretty bad performance and interop surprises, you shouldn't depend on it and instead use getElementById even if it's a bit more verbose.
It uses a property interceptor which is fairly slow in v8:
https://source.chromium.org/chromium/chromium/src/+/main:out/Debug/gen/third_party/blink/renderer/bindings/modules/v8/v8_window.cc;drc=a432cd59d51281057ba2a2673ca645a9600bb927;l=21590?q=file:gen%20window.cc%20file:blink&ss=chromium%2Fchromium%2Fsrc https://source.chromium.org/chromium/chromium/src/+/main:out...
to call this mess of security checks:
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc;drc=a432cd59d51281057ba2a2673ca645a9600bb927;l=186 https://source.chromium.org/chromium/chromium/src/+/main:thi...
which has this interop surprise:
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc;l=281;drc=a432cd59d51281057ba2a2673ca645a9600bb927 https://source.chromium.org/chromium/chromium/src/+/main:thi...
which in the end scans the document one element at a time looking for a match here:
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/window_name_collection.cc;l=25;drc=a432cd59d51281057ba2a2673ca645a9600bb927 https://source.chromium.org/chromium/chromium/src/+/main:thi...
In contrast getElementById is just a HashMap lookup, only does scanning if there's duplicates for that id, and never surprisingly returns a list!
- goatlover 4y agoIs there a reason to not use querySelector, since it’s a lot more flexible? One reason jQuery became so popular is because the DOM was painful to use. Things like querySelector fix that.
- masswerk 4y agoBTW, in my experience getElementById() is still fastest.
- eyelidlessness 4y agoIn isolation definitely, but in real world code it might be faster to use querySelector for branchy code if it doesn’t always use an id. As with everything, if it’s not performance-sensitive write the code that’s easier for humans to read, and if it is measure first.
- olliej 4y agoI'm not sure what you're trying to say here, as it's tautologically correct that getElementById can't be used in cases where you want to select on more than just the id. Do you mean a use case where you have branchy code that produces a selector string that has some id only paths?
- eyelidlessness 4y agoYes. Branchy code which could sometimes use getElementById and other times use querySelector may be faster if it always uses querySelector, even if that call itself is slower. The reason for this is that the JITs sometimes deoptimize on branchy logic with inconsistent property access between branches. They also deoptimize on branchy logic defining intermediate values, but much less often when the value is a consistent type like a string (selector).
- olliej 4y agoThis would only be relevant if you're doing something like var theFunction = condition ? "querySelector" : "getElementById"; ... document[theFunction](...) it won't apply to if (condition) document.querySelector(...) else document.getElementById(...) As from the point of view of the runtime the latter has two call sites, and each one is monomorphic and will very quickly (first layer of the JIT tower generally) become a Structure/Shape/HiddenClass check on `document` followed by a direct call to the host environment's implementation function (or more likely the argument checking and marshaling function before the actual internal implementation). It is possible that the higher level JITs pay attention to the branch counts on conditions or use other side channels for the deopt, but for host functions it's generally not something that will happen as the JITs see natively implemented functions as largely opaque barriers - they only have a few internal (to the runtime itself) cases where they make any assumptions about the behaviour of host functions.
- devmor 4y agoThe performance difference is negligible. Both methods can return 70k-100k selections in 10ms.
- masswerk 4y agoI had projects where this contributed to a visibly perceivable difference. This may have involved SVG, though.
- devmor 4y agoOh I can definitely see that coming up with SVG or deep extensive XML trees yeah.
- esprehn 4y agoOn what hardware? Also there's a performance cliff when you have a lot of unique ids (or selectors in use from JS). When you hit the cache querySelector is primarily a getElementById call and then some overhead to match the selector a second time (which chrome should really optimize): https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/selector_query.cc;drc=a432cd59d51281057ba2a2673ca645a9600bb927;l=347 https://source.chromium.org/chromium/chromium/src/+/main:thi... But if you have more than 256 selectors and ids in use: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/selector_query.cc;drc=a432cd59d51281057ba2a2673ca645a9600bb927;l=497 https://source.chromium.org/chromium/chromium/src/+/main:thi... You'll start to hit the selector parser a lot more and then querySelector will be a fair bit slower going through the CSS parser.
- olliej 4y agoThat’s surprising, webkit+blink and I’m guessing gecko all optimize the query selector cases. I assume it’s the cost of the NodeList (because NodeLists are live :-/)
- masswerk 4y agoMay be worth testing it against getElementsByClassName(), which also returns a live collection.
- olliej 4y agoI actually just went and tested and in webkit at least my 100% perfect test case I had querySelector taking 2x longer than getElementById. I tried understanding what the current webkit code does but the selector matching code is now excitingly complex due to the CSS JIT. Many many years ago I recall querySelector starting out with a check for #someCSSIdentifier and shortcutting to the getElementById path, but maybe my memory is playing tricks on me.
- esprehn 4y agoYup that's what it did, and Chrome still does. After much research and prototyping the CSS JIT didn't improve real world content (especially given the complexity) so it was never added to Chrome.
- dspillett 4y ago> Is there a reason to not use querySelector getElement is slightly faster, but not by enough to care IIRC so I use querySelector for consistency and it's flexibility. > One reason jQuery became so popular is because the DOM was painful I would say that is the key reason, with everything else being collateral benefits. Assuming you combine element selection, dealing with legacy incompatibilities, and function chaining to reduce boilerplate code, under the same banner of "making the DOM less painful".
- throwaway0asd 4y agoQuerySelectors are slow. Epic slow. I would also argue that querySelectors are far less flexible and became popular because they are instead easy. https://jsbench.github.io/#b39045cacae8d8c4a3ec044e538533dc https://jsbench.github.io/#b39045cacae8d8c4a3ec044e538533dc ProTip: Without numbers performance opinions are wrong by several orders of magnitude 80% of the time.
- wruza 4y agoI’m getting 70mops byId and 23mops qs-#id. This looks like making a huge difference until I add these cases: 3: "abc”.replace("a", "b") 4: "1" * 2 Which result in 15mops and 74mops respectively. This test measures diameters of neutrinos so to say.
- throwaway0asd 4y agoMy experience is that most developers tend to guess at performance and throw away numbers they disagree with. As a result performance testing is only something product owners care about.
- toddmorey 4y agoI really wish in the source code it was actually named globalScopePolluter()