microsoft / vscode

Visual Studio Code
https://code.visualstudio.com
MIT License
162.38k stars 28.62k forks source link

Window hang when rendering deeply nested trees #213839

Open roblourens opened 3 months ago

roblourens commented 3 months ago

When I disable the YAML extension, this doesn't repro

roblourens commented 3 months ago

Stack that might be infinitely recursive

F (indexTreeModel.ts:705)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
F (indexTreeModel.ts:711)
getNode (indexTreeModel.ts:755)
getParentNodeLocation (objectTreeModel.ts:305)
w (abstractTree.ts:503)
v (abstractTree.ts:487)
renderElement (abstractTree.ts:417)
renderElement (listWidget.ts:1249)
Z (listView.ts:940)
(anonymous) (listView.ts:876)
transact (rowCache.ts:80)
Y (listView.ts:867)
ib (listView.ts:1107)
y (event.ts:1196)
z (event.ts:1207)
fire (event.ts:1231)
(anonymous) (scrollableElement.ts:216)
y (event.ts:1196)
fire (event.ts:1227)
n (scrollable.ts:393)
setScrollPositionNow (scrollable.ts:303)
setScrollPosition (scrollableElement.ts:619)
setScrollTop (listView.ts:1030)
reveal (listWidget.ts:1909)
reveal (abstractTree.ts:2989)
H (outlinePane.ts:332)
y (event.ts:1196)
z (event.ts:1207)
fire (event.ts:1231)
p (documentSymbolsOutline.ts:404)
n (documentSymbolsOutline.ts:370)
await in n (async)
(anonymous) (documentSymbolsOutline.ts:189)
y (event.ts:1196)
z (event.ts:1207)
fire (event.ts:1218)
(anonymous) (codeEditorWidget.ts:275)
y (event.ts:1196)
fire (event.ts:1227)
D (editorConfiguration.ts:110)
updateOptions (editorConfiguration.ts:167)
updateOptions (codeEditorWidget.ts:414)
Mb (textCodeEditor.ts:49)
Qb (textEditor.ts:323)
(anonymous) (textEditor.ts:188)
y (event.ts:1196)
z (event.ts:1207)
fire (event.ts:1231)
(anonymous) (codeEditorWidget.ts:1730)
y (event.ts:1196)
fire (event.ts:1227)
s (viewModelEventDispatcher.ts:64)
emitOutgoingEvent (viewModelEventDispatcher.ts:39)
(anonymous) (viewModelImpl.ts:442)
y (event.ts:1196)
z (event.ts:1207)
fire (event.ts:1231)
setLanguageId (tokenizationTextModelPart.ts:331)
Fb (textModel.ts:1956)
setLanguage (textModel.ts:1951)
z (textEditorModel.ts:98)
F (textEditorModel.ts:134)
await in F (async)
(anonymous) (textEditorModel.ts:119)
queue (async.ts:231)
(anonymous) (async.ts:423)
(anonymous) (async.ts:364)
Promise.then (async)
trigger (async.ts:358)
trigger (async.ts:423)
D (textEditorModel.ts:119)
mb (untitledTextEditorModel.ts:406)
(anonymous) (untitledTextEditorModel.ts:376)
(anonymous) (textModel.ts:231)
y (event.ts:1196)
z (event.ts:1207)
fire (event.ts:1231)
endDeferredEmit (textModel.ts:2508)
pushEditOperations (textModel.ts:1273)
c (cursor.ts:795)
executeCommands (cursor.ts:753)
G (cursor.ts:359)
(anonymous) (cursor.ts:595)
L (cursor.ts:519)
paste (cursor.ts:594)
(anonymous) (viewModelImpl.ts:1061)
S (viewModelImpl.ts:1107)
R (viewModelImpl.ts:1043)
paste (viewModelImpl.ts:1061)
Wb (codeEditorWidget.ts:1149)
trigger (codeEditorWidget.ts:1075)
I (copyPasteController.ts:535)
await in I (async)
(anonymous) (copyPasteController.ts:320)
await in (anonymous) (async)
b (async.ts:27)
z (copyPasteController.ts:302)
w (copyPasteController.ts:293)
(anonymous) (copyPasteController.ts:105)
(anonymous) (clipboard.ts:229)
runCommand (editorExtensions.ts:229)
handler (editorExtensions.ts:155)
invokeFunction (instantiationService.ts:109)
n (commandService.ts:95)
executeCommand (commandService.ts:60)
M (abstractKeybindingService.ts:370)
J (abstractKeybindingService.ts:225)
(anonymous) (keybindingService.ts:281)
roblourens commented 3 months ago

I see that we end up with every row producing a symbol that is a child of the preceeding row and I think we get really inefficient at rendering extremely deeply nested trees.

I can't repro the hang in Stable, but I do see that the outline view doesn't render properly and things are slow

roblourens commented 3 months ago

I'm a little confused because it's easily reproable and seems worse in Insiders, Logan and Devin confirmed this too, but I don't see any interesting changes in any relevant outline view or tree files for months or years, so any ideas @jrieken or @joaomoreno?

joaomoreno commented 3 months ago

Not a recent regression, since it repros in Stable, so moving it to June.

After a few profiling runs, I could find two culprits here: sticky scrolling and tree indent guides. The issue no longer reproduces if you disable both of those features in the user settings. Also, even after disabling both those features, a profile of scrolling all the way down and then all the way up, while somewhat smooth, still reveals some fat frames (70ms on my M2 machine) which could definitely be optimized. The deep stacks are not infinite but just super deep and are the result of these two recursive functions: https://github.com/microsoft/vscode/blob/f10898df1d33b0cb8486e2cade6713f952ac6fe7/src/vs/base/browser/ui/tree/indexTreeModel.ts#L684-L713 (funny how they claim they are cheap 😆). We should refactor these to be iterative instead of recursive and see if that helps with the runtime of getNode.

Finally, I think we should also follow up with the Red Hat so they figure out a better strategy of reporting outline information on such a file which clearly isn't valid YAML straight from the first character.

roblourens commented 3 months ago

To be clear, were you able to repro on stable? I was never able to repro a full hang on stable, with all the same settings and extensions. But I can believe that it should be the same in theory.

And our language detection always seems to love detecting 'yaml' on files that are not yaml.

joaomoreno commented 3 months ago

Yeah I did repro and profile on stable only. 👌

And our language detection always seems to love detecting 'yaml' on files that are not yaml.

Yeah maybe another good follow up!