Describe the bug
UncontrolledTreeEnvironment builds the item map like this:
items
.map(item => ({ [item.index]: item }))
.reduce((a, b) => ({ ...a, ...b }), {})
Each step copies the whole accumulator, so loading N items does around N²/2 property writes instead of N. It also grows every intermediate object one property at a time, which puts them into dictionary mode and creates a lot of garbage.
The same code is in two places:
packages/core/src/uncontrolledEnvironment/UncontrolledTreeEnvironment.tsx lines 69-72, in the onDidChangeTreeData handler
- same file, lines 223-226, in
onMissingItems
This is fine on small trees, but gets very bad on large ones.
To Reproduce
Use a TreeDataProvider whose getTreeItems returns a large number of items in one call. In our case the tree grew to roughly 40,000 items as data loaded in, and the page became unresponsive for minutes.
A Firefox profile of that load showed 277 seconds out of 279 spent in Object.assign under the reduce, plus about 59 seconds of major GC.
Replacing the reduce with a plain loop:
const itemMap = {};
for (const item of items) {
itemMap[item.index] = item;
}
brought the same load down to 134 ms.
Expected behavior
The item map is built in a single pass, without copying the accumulator for every item.
Additional context
- macOS, Firefox
- 2.6.0, and the same code is in 2.6.1
Smaller, related: writeItems (line 43) does { ...oldItems, ...newItems }, which copies the whole map on every call. That is cheap when items arrive in a few large batches, but it means fetching in many small batches costs more than it looks like it should.
Describe the bug
UncontrolledTreeEnvironmentbuilds the item map like this:Each step copies the whole accumulator, so loading N items does around N²/2 property writes instead of N. It also grows every intermediate object one property at a time, which puts them into dictionary mode and creates a lot of garbage.
The same code is in two places:
packages/core/src/uncontrolledEnvironment/UncontrolledTreeEnvironment.tsxlines 69-72, in theonDidChangeTreeDatahandleronMissingItemsThis is fine on small trees, but gets very bad on large ones.
To Reproduce
Use a
TreeDataProviderwhosegetTreeItemsreturns a large number of items in one call. In our case the tree grew to roughly 40,000 items as data loaded in, and the page became unresponsive for minutes.A Firefox profile of that load showed 277 seconds out of 279 spent in
Object.assignunder the reduce, plus about 59 seconds of major GC.Replacing the reduce with a plain loop:
brought the same load down to 134 ms.
Expected behavior
The item map is built in a single pass, without copying the accumulator for every item.
Additional context
Smaller, related:
writeItems(line 43) does{ ...oldItems, ...newItems }, which copies the whole map on every call. That is cheap when items arrive in a few large batches, but it means fetching in many small batches costs more than it looks like it should.