text with lots of dom nodes makes text-selection take multiple seconds in webrender
Categories
(Core :: DOM: Selection, enhancement, P3)
Tracking
()
Tracking | Status | |
---|---|---|
firefox65 | --- | affected |
People
(Reporter: Gankra, Assigned: jjaschke)
References
(Depends on 2 open bugs, Blocks 2 open bugs)
Details
Attachments
(3 files)
Updated•6 years ago
|
Reporter | ||
Comment 1•6 years ago
|
||
Reporter | ||
Updated•6 years ago
|
Comment 2•6 years ago
|
||
Reporter | ||
Comment 3•6 years ago
|
||
Reporter | ||
Comment 4•6 years ago
|
||
Comment 6•6 years ago
|
||
Comment 7•6 years ago
|
||
Reporter | ||
Comment 8•6 years ago
|
||
Updated•6 years ago
|
Comment 9•6 years ago
|
||
Updated•6 years ago
|
Comment 10•6 years ago
|
||
Comment 11•6 years ago
|
||
Updated•6 years ago
|
Updated•6 years ago
|
Updated•6 years ago
|
Updated•5 years ago
|
Updated•2 years ago
|
Comment 12•5 months ago
|
||
Retesting after After bug 1867249 has landed.
Action: Select some lines of text and then scroll on the page.
Profile of the testcase : https://share.firefox.dev/3WDDK1c
Profile of testcase with 10x the loop count: https://share.firefox.dev/4fwydlP
:jjaschke, is there anything to improve here further?
Assignee | ||
Comment 13•5 months ago
|
||
Attached a testcase with 50k nodes instead of just 5k for testing.
I've looked into the profiles and this is my hypothesis of what's causing the bad performance here:
The cache implemented in bug 1867249 deals with fully selected nodes. To check if a node is fully selected now only takes a hashtable lookup instead of comparing the node's position to the range start/end positions. Comparing the node's position to start/end of the range involves computing the index of the node in its parent, which is a O(n)
operation (linked list).
The performance problems we see in the latest profile are now only the partially selected nodes. Since dragging a selection updates the end point of the range constantly, the painting code has to recompute the selection for (almost) every frame. Since the last selected node is likely only partially selected, it falls back to the old implementation and compares its position to range start/end.
The farther down one scrolls, the more expensive this gets, as this example is basically one parent node with a lot of child nodes. I think this is noticeable - dragging the selection on the top of the view-source isn't too bad, but if you scroll down while dragging the selection, it gets increasingly less responsive.
I have an idea that I want to try out to overcome this. I'm wondering if it's possible to have a node->index cache (ie., hashtable) in the document. The cache would need to be cleared each time a DOM mutation happens, and given that hashtables have an overhead on their own, it's likely that there needs to be a threshold for when to use it.
Whenever nsINode::ComputeIndexOf() is being called:
- first, do the checks that happen (is child a child of this, is it the first/last child)
- if the cache is active (ie.
mChildCount > THRESHOLD
)- look up
aPossibleChild
in the cache, if found, return its index - otherwise, call
ComputeIndexOf(aPossibleChild->GetPreviousSibling())
, which starts a recursion until we have a cache hit or reach the first child - add 1 to the returned index of the previous node, insert it into the cache and return it
- look up
- Otherwise, run old code path
This would introduce a to-be-measured initial overhead due to the hashtable lookups/insertions. But a subsequent call would only need to traverse the siblings until a cache hit, which should be faster than traversing to the start/end. And if there's a direct cache hit, computing the index is just one hashtable lookup. For the given situation where no DOM mutations happen, the cache would only be populated once. This should be faster overall... let's find out! :)
Mayank, thanks for bringing this to my attention!
Assignee | ||
Comment 14•5 months ago
|
||
Here's a profile of the 50k nodes test case after implementing the mentioned cache: https://share.firefox.dev/46CJZXE
This is way better than before, so the general idea seems to be promising.
Assignee | ||
Comment 15•5 months ago
|
||
Comment 16•5 months ago
•
|
||
The try build crashes for me if I open the 50k testcase and do a "Ctrl + A".
Unfortunately, the crash reporter doesnt submit these crashes. And it is not symbolicated.
Assignee | ||
Comment 17•5 months ago
|
||
Can't repro on my local build. What build did you use?
Comment 18•5 months ago
•
|
||
Assignee | ||
Comment 19•5 months ago
|
||
Ah, it's likely too much recursion depth. :) I'm preparing a patch that works without recursion.
Updated•5 months ago
|
Comment 20•5 months ago
|
||
Try build makes the testcase from bug 1892514 very slow (I waited for ~1 minute before giving up).
Assignee | ||
Comment 21•5 months ago
|
||
Thanks for bringing this up. It’s not unexpected that I missed some cases. Maybe I should land the patch behind a pref so I can search for bad cases without affecting everyone else.
Assignee | ||
Comment 22•5 months ago
|
||
Here's what I found:
- Open this attachment, copy content, paste into external app (eg. VSCode)
- Select all text in VSCode, copy
- Open the codepen example
- Paste
- Scroll around
- Select all and delete
Profile (local build/macOS): https://share.firefox.dev/3ArzEC3
There's a 43ish second jank visible when pasting the content. It looks like this is running a code path which keeps clearing the cache and forces it to be rebuilt constantly. I'll investigate if I can add the batching there as well. Thank you!
Assignee | ||
Comment 23•5 months ago
|
||
Is it just me, or does the codepen example only highlight the first line after pasting? The original code is highlighted throughout, but after pasting the d3.js code, only the first line seems to be highlighted. This happens in local build, current Nightly, and Release, so it should be unrelated to what we're doing here.
Masayuki, I added the node index cache batching guard here. This solves the issue and pasting happens instantly (<1s). Is it safe to do that or would it break other things?
Assignee | ||
Comment 24•5 months ago
|
||
Ah, when replacing the pasted code with pasting jQuery, the code is highlighted properly. So this not working properly with the original example is likely a bug in the codepen code.
Assignee | ||
Comment 25•5 months ago
|
||
Mayank, here is a try run with the latest changes in case you want to follow along. (ni? for visibility)
Comment 26•5 months ago
•
|
||
(In reply to Jan Jaeschke [:jjaschke] from comment #25)
Mayank, here is a try run with the latest changes in case you want to follow along. (ni? for visibility)
STR from the 50k testcase on the current bug:
Selecting some text and scrolling from the scrollbar: https://share.firefox.dev/4dK4GTX
Selecting all the text and scrolling from the scrollbar: https://share.firefox.dev/3AmqV46
Selecting text by dragging mouse to the bottom of the screen and letting the page scroll: https://share.firefox.dev/3M6LKTB
Rapidly selecting and deselecting some text: https://share.firefox.dev/3SPqHZq
STR from comment #22 : https://share.firefox.dev/3yNlgDu
STR from bug 1912069 : https://share.firefox.dev/4dmVzbT
Edit: updated all the profiles.
Comment 27•5 months ago
|
||
Replied in Phablicator.
https://phabricator.services.mozilla.com/D218755#inline-1218332
On the other hand here is a right place for the patch?
Description
•