Files

128 lines
3.8 KiB
C++

#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 <chrono>
#include <string>
#include <vector>
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<ScenarioResult>& results() const { return results_; }
private:
Config cfg_;
std::vector<ScenarioResult> results_;
bool done_ = false;
// Backing storage for synthetic dataset (row-major strings).
std::vector<std::string> backing_;
std::vector<const char*> 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<double> 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