Skip to content

perf: track deepClone circular refs with a WeakMap#4734

Open
lukecotter wants to merge 1 commit into
tabulator-tables:masterfrom
lukecotter:perf-deepclone-circular-refs
Open

perf: track deepClone circular refs with a WeakMap#4734
lukecotter wants to merge 1 commit into
tabulator-tables:masterfrom
lukecotter:perf-deepclone-circular-refs

Conversation

@lukecotter

@lukecotter lukecotter commented Apr 11, 2025

Copy link
Copy Markdown
Contributor

What

Track circular references in Helpers.deepClone with a WeakMap (O(1)) instead of an array scanned per object (O(n)). Record each source object → its clone at entry, and look it up before recursing.

Behaviour

Unchanged for normal clones. Also fixes two circular-reference bugs: the array version never recorded the root (a ref back to the root was cloned as a duplicate), and an earlier draft keyed the map by the parent but looked up the child. New Helpers.spec.js tests cover shared + nested + root circular references. Full suite green.

Performance (isolated, Node, deepClone of a wide graph)

nodes before after speedup
5,000 73.6 ms 1.9 ms ~38×
20,000 1396 ms 9.5 ms ~147×
circular chain 3,000 7.9 ms 0.8 ms ~10×

O(n²) → O(n).

Grid impact

Negligible — deepClone runs off the render path (Accessor/Localize), and grid rows are small/non-circular (accessor getData over 20k rows moved ~7%). The large win is for big/deep/circular data structures. Baseline upstream/master @ 9539446.

@rathboma

Copy link
Copy Markdown
Collaborator

@lukecotter This does change the behavior though right? It changes it from using .copy to using the reference directly. Is that intentional? What are the side effects of that?

@lukecotter

Copy link
Copy Markdown
Contributor Author

@rathboma oof I forgot to add to the map. It’s in my local copy I will sort asap. Sorry about that.

Replace the O(n) array scan (findIndex per visited object) with an O(1) WeakMap keyed by each source object -> its clone. On large graphs this is ~38x faster at 5k nodes and ~147x at 20k (O(n^2) -> O(n)), matching the reported >5min -> <3s on very large circular graphs.

Also fixes a latent correctness bug: the previous list never recorded the root object, so a reference back to the root was cloned as a duplicate object instead of resolving to the same clone. Keying every source object (including the root) preserves shared and circular reference identity.

Adds Helpers.deepClone unit tests (deep copy, mutation isolation, shared refs, nested and root circular references).
@lukecotter lukecotter force-pushed the perf-deepclone-circular-refs branch from c293c0f to 55ff567 Compare July 2, 2026 20:39
@lukecotter lukecotter changed the title perf: speed up deepClone with large number of circular refs perf: track deepClone circular refs with a WeakMap Jul 2, 2026
@lukecotter

Copy link
Copy Markdown
Contributor Author

Part of #4917 (overview + combined numbers).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants