Skip to content

perf(common): improve Select All row selections toggling on large dataset by 9x faster - #2785

Merged
ghiscoding merged 7 commits into
masterfrom
perf/select-all-large
Sep 11, 2026
Merged

perf(common): improve Select All row selections toggling on large dataset by 9x faster#2785
ghiscoding merged 7 commits into
masterfrom
perf/select-all-large

Conversation

@ghiscoding

@ghiscoding ghiscoding commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Summary

Improve checkbox Select All performance for large local datasets while preserving normal row-selection behavior and paginated all-pages selection state.

In the 500K-row benchmark, Select All is approximately 9× faster than master. The final implementation keeps compact range handling private to SlickGrid, avoids duplicate DataView/grid selection work, and includes regression fixes found through Cypress and unit coverage.

Why

SlickGrid virtualizes DOM rendering, so the 500K-row cost was primarily main-thread selection bookkeeping rather than DOM creation.

On master, the expensive work included:

  • syncing selected IDs by scanning the full filtered dataset;
  • creating one SlickRange per selected row;
  • allocating selected-cell CSS hashes for rows that are not rendered;
  • duplicate DataView/grid selection event and update work.

The checkbox Select All path already produces ascending, unique row indexes, which makes it safe to compact contiguous rows into a small number of ranges.

Changes

  • Compact ascending contiguous rows into SlickRange instances only for checkbox click.selectAll.

    • 500K contiguous rows become one range instead of 500K ranges.
    • Disjoint selectable rows remain separate ranges.
    • Non-ascending or duplicate caller input falls back to legacy per-row ranges and legacy ordering.
  • Keep range compaction private to SlickGrid.

    • No new public Core helper or API was added.
    • SlickHybridSelectionModel keeps its existing range conversion behavior.
  • Reduce selected-cell CSS work for non-rendered rows.

    • Selection CSS hashes are created only for rendered rows.
    • Selected styling is applied as rows render.
    • Selected styling now coexists with other cell CSS overlays.
  • Reduce selected-row diff work for bulk selection flows.

    • Valid bulk Select All/Unselect All ranges avoid the legacy large-array sort.
    • Non-bulk and non-ascending selections preserve legacy ordering behavior.
    • Selected-row differences are calculated using sets.
  • Avoid duplicate DataView/grid selection work.

    • DataView selected IDs remain populated for paginated Select All.
    • The duplicate DataView selected-IDs event is suppressed when the checkbox flow immediately applies the corresponding grid selection.
    • The precomputed filtered-ID cache is tied to the exact selected-ID state that created it, preventing stale selection state from being published.
  • Preserve selection styling and state compatibility.

    • Normal checkbox row toggles, Ctrl selection, keyboard selection, programmatic selection, and Hybrid Selection Model behavior remain unchanged.
    • Existing filtered and paginated selected-row behavior is preserved.

Validation

  • TypeScript diagnostics passed:

    node_modules\.bin\tsc.cmd -p packages/common/tsconfig.json --noEmit
    
  • Prettier validation passed.

  • git diff --check passed.

  • Focused selection suites passed:

    packages/common/src/core/__tests__/slickCore.spec.ts
    packages/common/src/core/__tests__/slickDataView.spec.ts
    packages/common/src/core/__tests__/slickGrid.spec.ts
    packages/common/src/extensions/__tests__/slickCheckboxSelectColumn.spec.ts
    packages/common/src/extensions/__tests__/slickHybridSelectionModel.spec.ts
    

    Result: 808 tests passed.

  • Added regression coverage for:

    • compact contiguous Select All ranges;
    • disjoint and empty selections;
    • non-ascending and duplicate row input;
    • legacy normal row-selection ordering;
    • selected CSS rendering without a CSS hash;
    • selected CSS rendering alongside another cell CSS overlay;
    • DataView filtered selected IDs after normal grid selection;
    • stale pending filtered IDs;
    • paginated Select All state.
  • Cypress regression follow-ups covered:

    • Example 10 multiple grids with row selection and pagination;
    • Example 14 paginated Select All behavior;
    • Example 32 Select All behavior across Angular, Aurelia, React, and Vue demos.

Browser Benchmark

Benchmarked in the Vanilla demo at:

http://localhost:8888/#/example03

The 500K-row dataset was loaded before clicking the checkbox Select All control. Timings were captured using console.time() instrumentation around the selection phases.

Select All: 500K Rows

Phase master This PR
Build grid rows ~23 ms ~25 ms
Sync DataView IDs ~2258 ms ~12 ms
grid.setSelectedRows() ~1678 ms ~386 ms
Total ~3960 ms ~423 ms

Unselect All: 500K Rows

Phase master This PR
Sync DataView IDs ~1908–2168 ms ~36–48 ms
grid.setSelectedRows() ~90–112 ms ~0–139 ms
Total ~1998–2280 ms ~48–175 ms

The original Vanilla benchmark reduced Select All from approximately 4 seconds to below 0.5 seconds, or roughly 9.4× faster and approximately 89% less time.

Unselect All improved from approximately 2 seconds to below 0.2 seconds, depending on the run.

Revalidated After Regression Fixes

The benchmark was repeated on a different computer using the same 500K-row workflow. The values below are averages from two page-reload runs. Raw milliseconds are hardware-specific, but the relative improvement remains consistent.

Operation master This PR Approximate improvement
Select All 8.68 s 0.99 s 8.8× faster
Unselect All 4.82 s 0.119 s 40.6× faster

The remaining Select All cost is mostly maintaining the public selected-row array and publishing the corresponding selection event for 500K rows.

Comments

The optimization is intentionally limited to the checkbox bulk Select All path.

Normal checkbox row toggles, Ctrl/keyboard selection, programmatic selection, and Hybrid Selection Model range behavior retain their existing observable behavior.

The framework Cypress updates are required because the optimized behavior lives in the common package and is shared by Angular, Aurelia, React, Vue, and Vanilla demos.

AI / LLM Assistance

  • AI / LLM assistance used:
    • No
    • Yes
  • If Yes:
    • Which tools/models: Claude and OpenAI Codex
    • How were they used: Code exploration, implementation assistance, performance analysis, regression triage, test and coverage additions, Cypress validation planning, benchmark analysis, and PR description drafting.

Checklist

  • The changes are limited to one scope: common checkbox bulk row selection and its regression coverage.
  • Tests were added or updated where appropriate.
  • Documentation was updated where appropriate; no public API or user-facing documentation changes were required.

@ghiscoding ghiscoding changed the title Perf/select all large perf(common): improve large row select-all handling Sep 10, 2026
@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (52be8cb) to head (458376e).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2785   +/-   ##
=======================================
  Coverage   100.0%   100.0%           
=======================================
  Files         199      199           
  Lines       25814    25867   +53     
  Branches     9144     9176   +32     
=======================================
+ Hits        25814    25867   +53     
Flag Coverage Δ
angular 100.0% <ø> (ø)
universal 100.0% <100.0%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pkg-pr-new

pkg-pr-new Bot commented Sep 10, 2026

Copy link
Copy Markdown
angular-slickgrid

npm i https://pkg.pr.new/angular-slickgrid@2785

aurelia-slickgrid

npm i https://pkg.pr.new/aurelia-slickgrid@2785

slickgrid-react

npm i https://pkg.pr.new/slickgrid-react@2785

slickgrid-vue

npm i https://pkg.pr.new/slickgrid-vue@2785

@slickgrid-universal/angular-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/angular-row-detail-plugin@2785

@slickgrid-universal/aurelia-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/aurelia-row-detail-plugin@2785

@slickgrid-universal/react-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/react-row-detail-plugin@2785

@slickgrid-universal/vue-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/vue-row-detail-plugin@2785

@slickgrid-universal/binding

npm i https://pkg.pr.new/@slickgrid-universal/binding@2785

@slickgrid-universal/common

npm i https://pkg.pr.new/@slickgrid-universal/common@2785

@slickgrid-universal/composite-editor-component

npm i https://pkg.pr.new/@slickgrid-universal/composite-editor-component@2785

@slickgrid-universal/custom-footer-component

npm i https://pkg.pr.new/@slickgrid-universal/custom-footer-component@2785

@slickgrid-universal/custom-tooltip-plugin

npm i https://pkg.pr.new/@slickgrid-universal/custom-tooltip-plugin@2785

@slickgrid-universal/empty-warning-component

npm i https://pkg.pr.new/@slickgrid-universal/empty-warning-component@2785

@slickgrid-universal/event-pub-sub

npm i https://pkg.pr.new/@slickgrid-universal/event-pub-sub@2785

@slickgrid-universal/excel-export

npm i https://pkg.pr.new/@slickgrid-universal/excel-export@2785

@slickgrid-universal/graphql

npm i https://pkg.pr.new/@slickgrid-universal/graphql@2785

@slickgrid-universal/odata

npm i https://pkg.pr.new/@slickgrid-universal/odata@2785

@slickgrid-universal/pagination-component

npm i https://pkg.pr.new/@slickgrid-universal/pagination-component@2785

@slickgrid-universal/pdf-export

npm i https://pkg.pr.new/@slickgrid-universal/pdf-export@2785

@slickgrid-universal/row-detail-view-plugin

npm i https://pkg.pr.new/@slickgrid-universal/row-detail-view-plugin@2785

@slickgrid-universal/rxjs-observable

npm i https://pkg.pr.new/@slickgrid-universal/rxjs-observable@2785

@slickgrid-universal/sql

npm i https://pkg.pr.new/@slickgrid-universal/sql@2785

@slickgrid-universal/text-export

npm i https://pkg.pr.new/@slickgrid-universal/text-export@2785

@slickgrid-universal/utils

npm i https://pkg.pr.new/@slickgrid-universal/utils@2785

@slickgrid-universal/vanilla-bundle

npm i https://pkg.pr.new/@slickgrid-universal/vanilla-bundle@2785

@slickgrid-universal/vanilla-force-bundle

npm i https://pkg.pr.new/@slickgrid-universal/vanilla-force-bundle@2785

@slickgrid-universal/web-mcp

npm i https://pkg.pr.new/@slickgrid-universal/web-mcp@2785

commit: 458376e

@ghiscoding ghiscoding changed the title perf(common): improve large row select-all handling perf(core): improve large row select-all handling Sep 10, 2026
@ghiscoding ghiscoding changed the title perf(core): improve large row select-all handling perf(common): improve Select All row selections perf by 8-10x for very large dataset Sep 10, 2026
@ghiscoding ghiscoding changed the title perf(common): improve Select All row selections perf by 8-10x for very large dataset perf(common): improve Select All row selections toggling by ~9x faster for very large dataset Sep 10, 2026
@ghiscoding ghiscoding changed the title perf(common): improve Select All row selections toggling by ~9x faster for very large dataset perf(common): improve Select All row selections toggling on large dataset by ~9x faster Sep 10, 2026
@ghiscoding ghiscoding changed the title perf(common): improve Select All row selections toggling on large dataset by ~9x faster perf(common): improve Select All row selections toggling on large dataset by 9x faster Sep 10, 2026
@ghiscoding
ghiscoding merged commit 0ab9f41 into master Sep 11, 2026
18 checks passed
@ghiscoding
ghiscoding deleted the perf/select-all-large branch September 11, 2026 00:10
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.

1 participant