#pragma once // bench_runner — headless benchmark harness for data_table::render(). // Issue 0133: perf gate for 10M-row refactor. // // Architecture: the Runner is driven frame-by-frame from within fn::run_app's // render callback. Each frame it calls data_table::render() for the current // scenario and measures the frame time. After duration_s seconds it moves to // the next scenario. After all 4 scenarios it signals completion. // // Usage in render callback: // static bench::Runner runner(cfg); // runner.tick(); // called every frame // if (runner.done()) { ... print + persist + close window ... } #include #include #include namespace bench { enum class Scenario { LinearScroll, // advance row offset each frame FilterLike, // filter name CONTAINS "foo" SortNumeric, // sort score col descending ColorRule, // NumericRange color rule on value col }; inline const char* scenario_name(Scenario s) { switch (s) { case Scenario::LinearScroll: return "linear_scroll"; case Scenario::FilterLike: return "filter_like"; case Scenario::SortNumeric: return "sort_numeric"; case Scenario::ColorRule: return "color_rule"; } return "unknown"; } struct ScenarioResult { Scenario scenario = Scenario::LinearScroll; double fps_p50 = 0.0; double fps_p1 = 0.0; double mem_rss_mb = 0.0; double cpu_pct = 0.0; double duration_s = 0.0; bool pass = false; }; struct Config { long long rows = 10000000; int cols = 20; double duration_s = 30.0; double fps_threshold = 60.0; std::string db_path = "operations.db"; std::string commit_sha; bool verbose = false; }; class Runner { public: explicit Runner(const Config& cfg); // Called once per render frame. Internally manages scenario transitions. // Returns true when all scenarios are finished. bool tick(); // Returns true when all scenarios have completed. bool done() const { return done_; } // Print JSON summary to stdout. void print_json() const; // Persist results into bench_runs SQLite table. void persist(const std::string& db_path) const; // Returns false if any scenario failed fps_threshold assertion. bool all_passed() const; const std::vector& results() const { return results_; } private: Config cfg_; std::vector results_; bool done_ = false; // Backing storage for synthetic dataset (row-major strings). std::vector backing_; std::vector ptrs_; // Per-scenario timing state. int current_scenario_ = 0; bool scenario_started_ = false; // Frame timing for current scenario. using Clock = std::chrono::steady_clock; Clock::time_point scenario_wall_start_; Clock::time_point last_frame_start_; std::vector frame_fps_; double user_start_ = 0.0; double sys_start_ = 0.0; // Scenario-specific state (rebuilt each scenario). struct ScenarioState; ScenarioState* sc_state_ = nullptr; int scroll_row_ = 0; static constexpr Scenario kScenarios[] = { Scenario::LinearScroll, Scenario::FilterLike, Scenario::SortNumeric, Scenario::ColorRule, }; static constexpr int kNumScenarios = 4; void seed_dataset(); void begin_scenario(Scenario s); void finish_scenario(); void render_table_frame(Scenario s); }; // Measure RSS memory in MB (Linux: /proc/self/status). double measure_rss_mb(); // Measure CPU % from RUSAGE delta over wall time. double measure_cpu_pct(double wall_s, double user_s, double sys_s); } // namespace bench