Summary
Profile-mode frame timings show VirtualResultGrid still does not fit the 120 Hz budget (8.33 ms) on a large, wide result, even after #875 (no re-sort on edit) and #879 (no setState per scroll tick). The remaining cost is widget build, not sorting or raster: every rebuild recreates all visible _DataRow / _GridCell widgets, including rows whose data did not change.
Measurements
Setup: flutter run --profile -d linux, 120 Hz display, 5000 rows × 120 columns, frame timings from SchedulerBinding.addTimingsCallback, 6 s per run. Baseline = dev before #875/#879. Synthetic stress: pan 14 px per frame, or one staged cell edit per frame on a grid sorted by column 0.
| Scenario |
build p50 |
frames over 8.33 ms |
frames in 6 s |
| Horizontal pan, before |
23.7 ms |
112 / 113 |
113 |
| Horizontal pan, after #879 |
0.65 ms |
50 / 157 |
157 |
| Edit on sorted grid, before |
28.3 ms |
113 / 114 |
114 |
| Edit on sorted grid, after #875 |
17.9 ms |
178 / 179 |
179 |
Raster is stable at about 4 ms (p50), i.e. half the budget on its own.
- Pan: most frames are cheap now, but about a third (the ones where the visible column window changes) take about 50 ms to build.
- Edit: a single cell edit still costs about 18 ms of build because all visible rows are rebuilt.
The benchmark app used for this is benchmark/grid_perf_bench.dart (MODE=scroll / MODE=edit); it is not in the repo yet.
Hypothesis (not yet verified)
_DataRow widgets are new instances on every build, so Element.updateChild cannot skip them, and each visible row rebuilds all of its _GridCells (hundreds to thousands of cells on a large window).
_GridCell is heavy per instance (gesture / tooltip / decoration layers), so even a cheap change multiplies.
Needed first: function-level profile
The numbers above only say build is slow. A function-level CPU profile (DevTools CPU profiler / Timeline in profile mode, on a full-screen window) is still required to confirm where the time goes before choosing a fix. Note the window size matters a lot: more visible cells means more build work, so the profile must be taken with a full-size (maximized) window.
Scope
- Capture the function-level profile for both scenarios and attach the top self-time frames.
- Stop rebuilding rows whose displayed data and status did not change (for example an
== on _DataRow keyed on a per-row version from the staging buffer, so Element.updateChild can skip it).
- Reduce per-cell cost in
_GridCell (fewer wrapper widgets / layers per cell).
- Re-run the benchmark and record before / after numbers in the PR.
- Optionally add
benchmark/grid_perf_bench.dart (or a trimmed version) so the measurement is repeatable.
Acceptance
- Edit on a sorted 5000 × 120 grid: build p90 under 8.33 ms in profile mode on a full-screen window.
- Horizontal pan: frames where the column window changes stay under 8.33 ms build.
- No change in selection, editing, staged-status colors or virtualization behaviour (existing grid tests stay green).
Out of scope
- Column auto-fit sampling in
LayoutBuilder.
- Changing the staging buffer's copy of original rows.
Summary
Profile-mode frame timings show
VirtualResultGridstill does not fit the 120 Hz budget (8.33 ms) on a large, wide result, even after #875 (no re-sort on edit) and #879 (nosetStateper scroll tick). The remaining cost is widget build, not sorting or raster: every rebuild recreates all visible_DataRow/_GridCellwidgets, including rows whose data did not change.Measurements
Setup:
flutter run --profile -d linux, 120 Hz display, 5000 rows × 120 columns, frame timings fromSchedulerBinding.addTimingsCallback, 6 s per run. Baseline = dev before #875/#879. Synthetic stress: pan 14 px per frame, or one staged cell edit per frame on a grid sorted by column 0.Raster is stable at about 4 ms (p50), i.e. half the budget on its own.
The benchmark app used for this is
benchmark/grid_perf_bench.dart(MODE=scroll/MODE=edit); it is not in the repo yet.Hypothesis (not yet verified)
_DataRowwidgets are new instances on every build, soElement.updateChildcannot skip them, and each visible row rebuilds all of its_GridCells (hundreds to thousands of cells on a large window)._GridCellis heavy per instance (gesture / tooltip / decoration layers), so even a cheap change multiplies.Needed first: function-level profile
The numbers above only say build is slow. A function-level CPU profile (DevTools CPU profiler / Timeline in profile mode, on a full-screen window) is still required to confirm where the time goes before choosing a fix. Note the window size matters a lot: more visible cells means more build work, so the profile must be taken with a full-size (maximized) window.
Scope
==on_DataRowkeyed on a per-row version from the staging buffer, soElement.updateChildcan skip it)._GridCell(fewer wrapper widgets / layers per cell).benchmark/grid_perf_bench.dart(or a trimmed version) so the measurement is repeatable.Acceptance
Out of scope
LayoutBuilder.