/** * Benchmark - Base classes for performance benchmarks * * Provides common infrastructure for measuring operation performance * including timing, iteration management, and result collection. * * Uses abstract classes with async methods instead of delegates, * since Vala doesn't support async delegates. * * @version 0.1 * @since 0.1 */ namespace Implexus.Tools.Perf { /** * Holds the results of a single benchmark operation. */ public class BenchmarkResults : Object { public string operation { get; set; } public int iterations { get; set; } public double total_time_ms { get; set; } public int batch_size { get; set; default = 0; } public double avg_time_ms { get { return iterations > 0 ? total_time_ms / iterations : 0; } } public double avg_item_time_ms { get { if (batch_size <= 0 || iterations <= 0) { return avg_time_ms; } int total_items = batch_size * iterations; return total_time_ms / total_items; } } public double ops_per_second { get { return avg_time_ms > 0 ? 1000.0 / avg_time_ms : 0; } } public double items_per_second { get { return avg_item_time_ms > 0 ? 1000.0 / avg_item_time_ms : 0; } } public int total_items { get { return batch_size > 0 ? batch_size * iterations : iterations; } } public BenchmarkResults() { operation = ""; iterations = 0; total_time_ms = 0; batch_size = 0; } } /** * Tracks paths for deferred cleanup. */ public class CleanupTracker : Object { private GLib.List _documents; private GLib.List _indices; private GLib.List _catalogues; private GLib.List _categories; private GLib.List _containers; public CleanupTracker() { _documents = new GLib.List(); _indices = new GLib.List(); _catalogues = new GLib.List(); _categories = new GLib.List(); _containers = new GLib.List(); } public void add_document(string path) { _documents.append(path); } public void add_index(string path) { _indices.append(path); } public void add_catalogue(string path) { _catalogues.append(path); } public void add_category(string path) { _categories.append(path); } public void add_container(string path) { _containers.append(path); } public unowned GLib.List get_documents() { return _documents; } public unowned GLib.List get_indices() { return _indices; } public unowned GLib.List get_catalogues() { return _catalogues; } public unowned GLib.List get_categories() { return _categories; } public GLib.List get_containers_reversed() { var reversed = new GLib.List(); foreach (unowned string path in _containers) { reversed.prepend(path); } return reversed; } public int get_total_count() { return (int)_documents.length() + (int)_indices.length() + (int)_catalogues.length() + (int)_categories.length() + (int)_containers.length(); } } /** * Abstract base class for async operations (replaces async delegates). */ public abstract class AsyncOperation : Object { public int iteration { get; set; } public abstract async void execute_async() throws Error; } /** * Abstract base class for benchmarks. */ public abstract class Benchmark : Object { public Core.Engine _engine; public BenchmarkConfig _config; public CleanupTracker _cleanup_tracker; public abstract string name { get; } protected Benchmark(Core.Engine engine, BenchmarkConfig config) { _engine = engine; _config = config; _cleanup_tracker = new CleanupTracker(); } public void set_cleanup_tracker(CleanupTracker tracker) { _cleanup_tracker = tracker; } public abstract async void run_async(Results results) throws Error; public void run(Results results) throws Error { var loop = new MainLoop(); Error? error = null; run_async.begin(results, (obj, res) => { try { run_async.end(res); } catch (Error e) { error = e; } loop.quit(); }); loop.run(); if (error != null) { throw (!) error; } } public async BenchmarkResults measure_async(string operation, int iterations, AsyncOperation op) throws Error { var timer = new Timer(); timer.start(); for (int i = 0; i < iterations; i++) { op.iteration = i; yield op.execute_async(); } timer.stop(); var elapsed = timer.elapsed() * 1000.0; return new BenchmarkResults() { operation = operation, iterations = iterations, total_time_ms = elapsed }; } public async void cleanup_path_async(Core.EntityPath path) { try { var entity = yield _engine.get_entity_or_null_async(path); if (entity != null) { yield ((!) entity).delete_async(); } } catch (Error e) { // Ignore cleanup errors } } public async Core.Entity ensure_container_async(Core.EntityPath path, string name) throws Error { var existing = yield _engine.get_entity_or_null_async(path); if (existing != null) { return (!) existing; } var parent_path = path.parent; Core.Entity parent; if (parent_path != null && !((!) parent_path).is_root) { parent = yield ensure_container_async((!) parent_path, ((!) parent_path).name); } else { parent = yield _engine.get_root_async(); } return yield parent.create_container_async(name); } } }