Repository navigation
perf(workspace): resolve UI freezes, unmemoized allocations, and isolate blocking in grid and inspector #702
Copy link
Copy link
Closed
Labels
enhancementNew feature or requestNew feature or requestfrontendTheme parser epic label: frontendTheme parser epic label: frontend
Description
Description
Following a performance and UI freeze audit of the workspace data grid, staging buffer, and value inspector, several bottlenecks causing UI freezes, main isolate blocking, and frame drops (jank) have been identified.
Identified Bottlenecks & Proposed Solutions
1. P0: Memory allocations & broken memoization in DataGridStagingBuffer.effectiveRows
- Files:
lib/features/workspace/data_grid_staging_buffer.dart,lib/features/workspace/results_tab.dart,lib/features/workspace/result_grid_view.dart - Problem:
effectiveRowsclones the entire dataset (List<String>.from) on every getter access. WhenResultsTab.build()passeseffectiveRowsto_getFilteredRows, the reference checkidentical(_memoEffectiveRows, effectiveRows)fails on every single rebuild (hover, cursor blink, focus change). This triggers full synchronous re-filtering (GridFilterEngine.filterRowIndices) on 50,000+ rows repeatedly. In addition, column auto-fit access triggers repeated list allocations. - Fix:
- In
DataGridStagingBuffer, returnbaseRowsdirectly when!isDirty(zero allocation, stable reference). - Cache
_cachedEffectiveRowswhen dirty, and invalidate only upon actual mutations (updateCell,deleteRow,addRow,revertAll).
- In
2. P1: Synchronous $O(N \log N)$ sorting on the main isolate
- Files:
lib/features/workspace/result_grid_view.dart - Problem:
sortResultGridRowsWithIndicesperforms key parsing and multi-column sorting synchronously on the UI thread. On tables with 50,000–100,000+ rows, sorting freezes the entire desktop UI for 500ms–2s+. - Fix:
- For small row counts (< 3000 rows), maintain fast synchronous sorting.
- For large row counts (>= 3000 rows), offload sorting to
compute/Isolate.runto keep UI fluid and responsive.
3. P1: GridGroupingsEngine.buildGroups executed directly in build()
- Files:
lib/features/workspace/data_grid_groupings_view.dart - Problem: Group tree generation, recursive map hashing, and aggregation calculations run directly inside
build(). Any parent rebuild or layout resize triggers expensive multi-pass iteration over all rows. - Fix: Cache the grouping tree (
_cachedGroups) in the state and only recompute when rows, group columns, or aggregates change (didUpdateWidget).
4. P2: Synchronous jsonDecode / XML validation on every keystroke
- Files:
lib/features/workspace/data_grid_value_panel.dart - Problem:
_validateContentexecutesjsonDecodeon every keystroke without debouncing. Editing large JSON/XML cells (100KB+) freezes typing and causes visual lag. - Fix: Add a 150ms debounce timer for validation parsing while typing.
Acceptance Criteria
-
DataGridStagingBuffer.effectiveRowsavoids allocations when clean and caches rows when dirty. -
ResultsTab._getFilteredRowsmemoization correctly preserves cache across frame rebuilds. - Large table sorting (>= 3000 rows) does not block the UI isolate.
-
DataGridGroupingsViewcaches grouping tree results outside ofbuild(). -
DataGridValuePaneldebounces validation parsing during text entry. - All workspace tests and analysis pass without regressions.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestfrontendTheme parser epic label: frontendTheme parser epic label: frontend
Activity
ZhuchkaTriplesix commentedon Sep 6, 2026
Resolved and merged into main via PR #703 and Release 0.4.15 (PR #705).