/** * Results - Results aggregation and output for benchmarks * * Collects benchmark results and provides formatted output * for comparing performance across different operations. * * @version 0.1 * @since 0.1 */ namespace Implexus.Tools.Perf { /** * Collects and displays benchmark results. * * Results are organized by benchmark name (e.g., "Container", "Document") * and can be output as a formatted summary table. */ public class Results : Object { /** * Internal storage for benchmark results. * Maps benchmark name to list of operation results. */ private Invercargill.DataStructures.Dictionary> _results; /** * Creates a new Results instance. */ public Results() { _results = new Invercargill.DataStructures.Dictionary>(); } /** * Adds a benchmark result. * * @param benchmark The benchmark name (e.g., "Container") * @param result The benchmark result to add */ public void add(string benchmark, BenchmarkResults result) { if (!_results.has(benchmark)) { _results.set(benchmark, new Invercargill.DataStructures.Vector()); } _results.get(benchmark).add(result); } /** * Gets all results for a specific benchmark. * * @param benchmark The benchmark name * @return An enumerable of results, or empty if not found */ public Invercargill.Enumerable get_results(string benchmark) { if (!_results.has(benchmark)) { return new Invercargill.DataStructures.Vector().as_enumerable(); } return _results.get(benchmark).as_enumerable(); } /** * Gets all benchmark names. * * @return An enumerable of benchmark names */ public Invercargill.Enumerable get_benchmark_names() { var names = new Invercargill.DataStructures.Vector(); foreach (var key in _results.keys) { names.add(key); } return names.as_enumerable(); } /** * Prints a formatted summary of all benchmark results. */ public void print_summary() { print("\n"); print("================================================================================\n"); print(" IMPLEXUS PERFORMANCE RESULTS \n"); print("================================================================================\n"); print("\n"); // Track overall statistics int total_operations = 0; double total_time = 0; foreach (var benchmark_name in _results.keys) { var results_vec = _results.get(benchmark_name); print("=== %s ===\n", benchmark_name); // Check if any results are batch operations bool has_batch = false; foreach (var result in results_vec) { if (result.batch_size > 0) { has_batch = true; break; } } if (has_batch) { // Extended format for batch operations print("%-30s %8s %10s %12s %12s %12s\n", "Operation", "Iters", "Batch", "Total(ms)", "Avg(ms)", "Items/sec"); print("--------------------------------------------------------------------------------------------------------\n"); foreach (var result in results_vec) { if (result.batch_size > 0) { // Batch operation - show batch details print("%-30s %8d %10d %12.2f %12.4f %12.2f\n", result.operation, result.iterations, result.batch_size, result.total_time_ms, result.avg_item_time_ms, result.items_per_second); } else { // Non-batch operation print("%-30s %8d %10s %12.2f %12.4f %12.2f\n", result.operation, result.iterations, "-", result.total_time_ms, result.avg_time_ms, result.ops_per_second); } total_operations += result.total_items; total_time += result.total_time_ms; } } else { // Standard format print("%-30s %10s %12s %15s\n", "Operation", "Iterations", "Avg (ms)", "Ops/sec"); print("--------------------------------------------------------------------------------\n"); foreach (var result in results_vec) { print("%-30s %10d %12.4f %15.2f\n", result.operation, result.iterations, result.avg_time_ms, result.ops_per_second); total_operations += result.iterations; total_time += result.total_time_ms; } } print("\n"); } // Print overall summary print("================================================================================\n"); print(" SUMMARY \n"); print("================================================================================\n"); print("Total operations: %d\n", total_operations); print("Total time: %.2f ms\n", total_time); print("Overall throughput: %.2f ops/sec\n", total_time > 0 ? (total_operations / total_time) * 1000 : 0); print("================================================================================\n"); } /** * Exports results as JSON for comparison or storage. * * @return A JSON string representation of the results */ public string to_json() { var sb = new StringBuilder(); sb.append("{\n"); bool first_benchmark = true; foreach (var benchmark_name in _results.keys) { var results_vec = _results.get(benchmark_name); if (!first_benchmark) { sb.append(",\n"); } first_benchmark = false; sb.append(" \"%s\": [\n".printf(escape_json_string(benchmark_name))); bool first_result = true; foreach (var result in results_vec) { if (!first_result) { sb.append(",\n"); } first_result = false; sb.append(" {\n"); sb.append(" \"operation\": \"%s\",\n".printf(escape_json_string(result.operation))); sb.append(" \"iterations\": %d,\n".printf(result.iterations)); if (result.batch_size > 0) { sb.append(" \"batch_size\": %d,\n".printf(result.batch_size)); sb.append(" \"total_items\": %d,\n".printf(result.total_items)); } sb.append(" \"total_time_ms\": %.4f,\n".printf(result.total_time_ms)); sb.append(" \"avg_time_ms\": %.4f,\n".printf(result.avg_time_ms)); if (result.batch_size > 0) { sb.append(" \"avg_item_time_ms\": %.4f,\n".printf(result.avg_item_time_ms)); sb.append(" \"items_per_second\": %.2f\n".printf(result.items_per_second)); } else { sb.append(" \"ops_per_second\": %.2f\n".printf(result.ops_per_second)); } sb.append(" }"); } sb.append("\n ]"); } sb.append("\n}\n"); return sb.str; } /** * Exports results to a file. * * @param path The file path to write to * @param format The output format ("text" or "json") * @throws Error if writing fails */ public void export(string path, string format = "text") throws Error { string content; if (format == "json") { content = to_json(); } else { content = format_as_text(); } FileUtils.set_contents(path, content); } /** * Formats results as plain text suitable for file output. * * @return A formatted text string */ private string format_as_text() { var sb = new StringBuilder(); sb.append("Implexus Performance Benchmark Results\n"); sb.append("======================================\n"); sb.append("Generated: %s\n\n".printf(new DateTime.now_utc().format("%Y-%m-%d %H:%M:%S UTC"))); foreach (var benchmark_name in _results.keys) { var results_vec = _results.get(benchmark_name); sb.append("[%s]\n".printf(benchmark_name)); foreach (var result in results_vec) { sb.append(" %s: %d iterations, %.4f ms avg, %.2f ops/sec\n".printf( result.operation, result.iterations, result.avg_time_ms, result.ops_per_second )); } sb.append("\n"); } return sb.str; } /** * Escapes a string for JSON output. * * @param s The string to escape * @return The escaped string */ private string escape_json_string(string s) { return s.replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t"); } /** * Calculates summary statistics for a benchmark. * * @param benchmark The benchmark name * @return A SummaryStats object with total_ops, total_time_ms, and avg_ops_per_sec */ public SummaryStats? get_summary_stats(string benchmark) { if (!_results.has(benchmark)) { return null; } int total_ops = 0; double total_time = 0; foreach (var result in _results.get(benchmark)) { total_ops += result.iterations; total_time += result.total_time_ms; } double avg_ops = total_time > 0 ? (total_ops / total_time) * 1000 : 0; return new SummaryStats(total_ops, total_time, avg_ops); } } /** * Holds summary statistics for a benchmark. */ public class SummaryStats : Object { /** * Total number of operations performed. */ public int total_ops { get; private set; } /** * Total time in milliseconds. */ public double total_time_ms { get; private set; } /** * Average operations per second. */ public double avg_ops_per_sec { get; private set; } /** * Creates a new SummaryStats instance. */ public SummaryStats(int total_ops, double total_time_ms, double avg_ops_per_sec) { this.total_ops = total_ops; this.total_time_ms = total_time_ms; this.avg_ops_per_sec = avg_ops_per_sec; } } }