Benchmark.vala 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /**
  2. * Benchmark - Base classes for performance benchmarks
  3. *
  4. * Provides common infrastructure for measuring operation performance
  5. * including timing, iteration management, and result collection.
  6. *
  7. * Uses abstract classes with async methods instead of delegates,
  8. * since Vala doesn't support async delegates.
  9. *
  10. * @version 0.1
  11. * @since 0.1
  12. */
  13. namespace Implexus.Tools.Perf {
  14. /**
  15. * Holds the results of a single benchmark operation.
  16. */
  17. public class BenchmarkResults : Object {
  18. public string operation { get; set; }
  19. public int iterations { get; set; }
  20. public double total_time_ms { get; set; }
  21. public int batch_size { get; set; default = 0; }
  22. public double avg_time_ms {
  23. get { return iterations > 0 ? total_time_ms / iterations : 0; }
  24. }
  25. public double avg_item_time_ms {
  26. get {
  27. if (batch_size <= 0 || iterations <= 0) {
  28. return avg_time_ms;
  29. }
  30. int total_items = batch_size * iterations;
  31. return total_time_ms / total_items;
  32. }
  33. }
  34. public double ops_per_second {
  35. get { return avg_time_ms > 0 ? 1000.0 / avg_time_ms : 0; }
  36. }
  37. public double items_per_second {
  38. get { return avg_item_time_ms > 0 ? 1000.0 / avg_item_time_ms : 0; }
  39. }
  40. public int total_items {
  41. get { return batch_size > 0 ? batch_size * iterations : iterations; }
  42. }
  43. public BenchmarkResults() {
  44. operation = "";
  45. iterations = 0;
  46. total_time_ms = 0;
  47. batch_size = 0;
  48. }
  49. }
  50. /**
  51. * Tracks paths for deferred cleanup.
  52. */
  53. public class CleanupTracker : Object {
  54. private GLib.List<string> _documents;
  55. private GLib.List<string> _indices;
  56. private GLib.List<string> _catalogues;
  57. private GLib.List<string> _categories;
  58. private GLib.List<string> _containers;
  59. public CleanupTracker() {
  60. _documents = new GLib.List<string>();
  61. _indices = new GLib.List<string>();
  62. _catalogues = new GLib.List<string>();
  63. _categories = new GLib.List<string>();
  64. _containers = new GLib.List<string>();
  65. }
  66. public void add_document(string path) { _documents.append(path); }
  67. public void add_index(string path) { _indices.append(path); }
  68. public void add_catalogue(string path) { _catalogues.append(path); }
  69. public void add_category(string path) { _categories.append(path); }
  70. public void add_container(string path) { _containers.append(path); }
  71. public unowned GLib.List<string> get_documents() { return _documents; }
  72. public unowned GLib.List<string> get_indices() { return _indices; }
  73. public unowned GLib.List<string> get_catalogues() { return _catalogues; }
  74. public unowned GLib.List<string> get_categories() { return _categories; }
  75. public GLib.List<string> get_containers_reversed() {
  76. var reversed = new GLib.List<string>();
  77. foreach (unowned string path in _containers) {
  78. reversed.prepend(path);
  79. }
  80. return reversed;
  81. }
  82. public int get_total_count() {
  83. return (int)_documents.length() + (int)_indices.length() +
  84. (int)_catalogues.length() + (int)_categories.length() +
  85. (int)_containers.length();
  86. }
  87. }
  88. /**
  89. * Abstract base class for async operations (replaces async delegates).
  90. */
  91. public abstract class AsyncOperation : Object {
  92. public int iteration { get; set; }
  93. public abstract async void execute_async() throws Error;
  94. }
  95. /**
  96. * Abstract base class for benchmarks.
  97. */
  98. public abstract class Benchmark : Object {
  99. public Core.Engine _engine;
  100. public BenchmarkConfig _config;
  101. public CleanupTracker _cleanup_tracker;
  102. public abstract string name { get; }
  103. protected Benchmark(Core.Engine engine, BenchmarkConfig config) {
  104. _engine = engine;
  105. _config = config;
  106. _cleanup_tracker = new CleanupTracker();
  107. }
  108. public void set_cleanup_tracker(CleanupTracker tracker) {
  109. _cleanup_tracker = tracker;
  110. }
  111. public abstract async void run_async(Results results) throws Error;
  112. public void run(Results results) throws Error {
  113. var loop = new MainLoop();
  114. Error? error = null;
  115. run_async.begin(results, (obj, res) => {
  116. try {
  117. run_async.end(res);
  118. } catch (Error e) {
  119. error = e;
  120. }
  121. loop.quit();
  122. });
  123. loop.run();
  124. if (error != null) {
  125. throw (!) error;
  126. }
  127. }
  128. public async BenchmarkResults measure_async(string operation, int iterations, AsyncOperation op) throws Error {
  129. var timer = new Timer();
  130. timer.start();
  131. for (int i = 0; i < iterations; i++) {
  132. op.iteration = i;
  133. yield op.execute_async();
  134. }
  135. timer.stop();
  136. var elapsed = timer.elapsed() * 1000.0;
  137. return new BenchmarkResults() {
  138. operation = operation,
  139. iterations = iterations,
  140. total_time_ms = elapsed
  141. };
  142. }
  143. public async void cleanup_path_async(Core.EntityPath path) {
  144. try {
  145. var entity = yield _engine.get_entity_or_null_async(path);
  146. if (entity != null) {
  147. yield ((!) entity).delete_async();
  148. }
  149. } catch (Error e) {
  150. // Ignore cleanup errors
  151. }
  152. }
  153. public async Core.Entity ensure_container_async(Core.EntityPath path, string name) throws Error {
  154. var existing = yield _engine.get_entity_or_null_async(path);
  155. if (existing != null) {
  156. return (!) existing;
  157. }
  158. var parent_path = path.parent;
  159. Core.Entity parent;
  160. if (parent_path != null && !((!) parent_path).is_root) {
  161. parent = yield ensure_container_async((!) parent_path, ((!) parent_path).name);
  162. } else {
  163. parent = yield _engine.get_root_async();
  164. }
  165. return yield parent.create_container_async(name);
  166. }
  167. }
  168. }