perf: a batch of small optimizations
I've been profiling Tabulator on large datasets and ended up with a handful of small, self contained performance PRs. Each one is behaviour preserving and stands on its own, but they're related so I figured a short overview in one place would help.
The PRs:
- #4904 – clone the row template and use
slice()for the active-rows copy — grid filter −23%;setActiveRows166× isolated - #4905 – avoid
arguments/forEachallocation in the event buses - grid render −36%; dispatch 7–9× isolated - #4906 – precompute sort keys and fast-path the number sorter — grid sort −31%;
_sortItems2.6× / number sorter 4.9× isolated - #4907 – reduce closure allocation in the filter and calc loops —
filterRowup to 3.8×,generateRowData2× isolated (negligible in a full grid) - #4908 – cache module refs and flatten the data-tree child recursion —
getChildren1.6× isolated (negligible in a full grid) - #4909 – reinitialize frozen-column rows in O(n) instead of O(n²) —
reinitializeRows1.5× isolated (negligible in a full grid) - #4724 – drop a redundant innerHTML write in cell content generation — render 2× / clear 2.6× isolated (negligible in a full grid)
- #4734 – track
deepClonecircular refs with a WeakMap (also fixes a circular ref clone bug) — 38–147× isolated (off the render path)
Everything was measured against master (9539446). A few of these move real grid work noticeably: with all of them applied, a typical render → sort → filter pass over 20k rows went from ~56ms to ~30ms (roughly 1.8× faster), mostly from the render and sort paths. The rest are smaller wins that are clear in isolation but negligible in a full grid render. I've called that out on each PR so it's obvious where the actual impact is. I also have numbers for a 250k row table which I will add asap.
Each PR has its own before/after numbers in the description. Happy to share the benchmark scripts or tweak anything. No rush, merge in whatever order works for you.
Source: tabulator-tables/tabulator