--- id: "0133" title: "data_table: optimizar para 10M filas sin caida de FPS (finalize modulo)" status: pendiente type: refactor domain: - cpp-stack - data-ingest scope: app-scoped priority: alta depends: [] blocks: [] related: - "0081" - "0097" created: 2026-05-22 updated: 2026-05-22 tags: [cpp, imgui, performance, data_table, finalize] flow: "" --- # 0133 — data_table 10M rows sin caida FPS **Status:** pendiente ## Por que `data_table_cpp_viz` (modulo `fn_module_data_table` / `fn_table_viz`) actualmente maneja decenas de miles de filas con `ImGuiListClipper` y rinde bien. Apps reales (call_monitor con telemetria, services_monitor con escalado, futuro graph_explorer con nodos) ya nos llevan a millones de filas. Objetivo: cerrar el modulo con benchmark estable de **10M filas a >=60fps** en hardware tipico (Ryzen 5 / i5 8th gen + 16GB). ## Que entrega Refactor del modulo manteniendo API publica + un benchmark suite. ### Cambios tecnicos 1. **Storage columnar** — hoy `std::vector>` row-major. Cambiar a column-major (`Column { type; vector data }`) para localidad de cache + iteracion. Las celdas se materializan solo para las filas visibles. 2. **String interning** — columnas de tipo string usan tabla de strings global con `uint32_t` indices. 10M filas con 50% strings repetidas → ahorra 60-70% RAM. 3. **Lazy filter/sort indices** — en vez de re-ordenar el storage, mantener `vector visible_rows` que apunta al storage subyacente. Filter/sort solo reescribe ese vector. 4. **Computed columns en bloques** — `compute_stage_cpp_core` ahora corre por cell; cambiar a procesar bloques de 1024 filas con SIMD via `OpenMP` (ya esta linkeado en fn_framework). 5. **Render path** — `ImGuiListClipper` sigue siendo el frontend, pero el callback de render no debe asignar memoria por fila. Pre-formatear strings de display en `column.display_cache[row_idx]` con LRU de 100k entradas; resto se formatea on-the-fly. 6. **Color rules** — `data_table_color_rules_cpp_viz` se evalua hoy por celda visible. Cachear el rule_id resuelto por row_idx tras primer paint. 7. **Stats** — `compute_column_stats_cpp_core` solo se recalcula cuando cambia el filtro, no cada frame. ### Benchmark suite `cpp/apps/data_table_bench/`: - Genera dataset sintetico 10M filas x 20 cols (mix int/float/string/timestamp). - Mide FPS sostenido durante: - scroll lineal full range (down → bottom). - filter por string match (`LIKE %foo%`). - sort por columna numerica. - color rule `value > p95`. - Output: `fps_p50`, `fps_p1`, `mem_rss_mb`, `cpu_pct`. - Asercion DoD: `fps_p1 >= 60` en cada escenario. ## DoD - Refactor entregado sin romper apps consumidoras (call_monitor, services_monitor, graph_explorer, navegator_dashboard, kanban_cpp future). - Benchmark suite ejecutable: `./data_table_bench --rows 10000000 --duration 30`. - Resultados de benchmark guardados en `apps/data_table_bench/operations.db` con assertion `fps_p1 >= 60`. - `e2e_checks` corriendo benchmark con dataset reducido (100k filas) en CI; full bench manual. - Modulo marcado `version: 1.0.0` y `tags: [stable]` en su `.md`. - Guia "porting old call sites" si la API publica cambia (en `cpp/functions/viz/data_table/MIGRATION.md`). ## Anti-scope - Sin GPU rendering (sigue siendo CPU + ImGui). - Sin paginacion remota (sigue todo in-memory). - Sin streaming append-while-rendering (snapshot al frame inicio). - Sin virtualizacion horizontal (todas las cols se renderizan; assumed N_cols <= 100). ## Notas Issue 0081 introdujo la migracion inline → modulo. Issue 0097 cerro el wrapping en fn_module/fn_table_viz. Esta issue es el **finalize**: lo deja `1.0.0` con benchmark + suficiente performance para que las apps de telemetria/graph no necesiten paginar manual.