I have a heavily optimized JavaScript app, a highly interactive graph editor. I now started profiling it (using Chrome dev-tools) with massive amounts of data (thousands of shapes in the graph), and I'm encountering a previously unusual performance bottleneck, Hit Test.
| Self Time | Total Time | Activity |
|-----------------|-----------------|---------------------|
| 3579 ms (67.5%) | 3579 ms (67.5%) | Rendering |
| 3455 ms (65.2%) | 3455 ms (65.2%) | Hit Test | <- this one
| 78 ms (1.5%) | 78 ms (1.5%) | Update Layer Tree |
| 40 ms (0.8%) | 40 ms (0.8%) | Recalculate Style |
| 1343 ms (25.3%) | 1343 ms (25.3%) | Scripting |
| 378 ms (7.1%) | 378 ms (7.1%) | Painting |
This takes up 65% of everything (!), remaining a monster bottleneck in my codebase. I know this is the process of tracing the object under the pointer, and I have my useless ideas about how this could be optimized (use fewer elements, use fewer mouse events, etc.).
Context: The above performance profile shows a "screen panning" feature in my app, where the contents of the screen can be moved around by dragging the empty area. This results in lots of objects being moved around, optimized by moving their container instead of each object individually. I made a demo.
Before jumping into this, I wanted to search for the general principles of optimizing hit testing (those good ol' "No sh*t, Sherlock" blog articles), as well as if any tricks exist to improve performance on this end (such as using translate3d
to enable GPU processing).
I tried queries like js optimize hit test, but the results are full of graphics programming articles and manual implementation examples -- it's as if the JS community hadn't even heard of this thing before! Even the chrome devtools guide lacks this area.
- Edit: there is this question, but it doesn't help much: What is the Chrome Dev Tools "Hit Test" timeline entry?
So here I am, proudly done with my research, asking: how do I get about optimizing native hit testing in JavaScript?
I prepared a demo that demonstrates the performance bottleneck, although it's not exactly the same as my actual app, and numbers will obviously vary by device as well. To see the bottleneck:
- Go to the Timeline tab on Chrome (or the equivalent of your browser)
- Start recording, then pan around in the demo like a mad-man
- Stop recording and check the results
A recap of all significant optimizations I have already done in this area:
- moving a single container on the screen instead of moving thousands of elements individually
- using
transform: translate3d
to move container - v-syncing mouse movement to screen refresh rate
- removing all possible unnecessary "wrapper" and "fixer" elements
- using
pointer-events: none
on shapes -- no effect
Additional notes:
- the bottleneck exists both with and without GPU acceleration
- testing was only done in Chrome, latest
- the DOM is rendered using ReactJS, but the same issue is observable without it, as seen in the linked demo
pointer-events: none
now works. At least in scrolling on Chrome 80. Scrolling was a performace bottleneck on my case. – Bradney