Harness.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Performance/Harness.h - C++ performance harness declaration
  2. //
  3. // This source file is part of the Swift.org open source project
  4. //
  5. // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
  6. // Licensed under Apache License v2.0 with Runtime Library Exception
  7. //
  8. // See http://swift.org/LICENSE.txt for license information
  9. // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
  10. //
  11. // -----------------------------------------------------------------------------
  12. ///
  13. /// Declaration for the C++ performance harness class.
  14. ///
  15. // -----------------------------------------------------------------------------
  16. #ifndef HARNESS_H
  17. #define HARNESS_H
  18. #import <chrono>
  19. #import <functional>
  20. #import <iostream>
  21. #import <map>
  22. #import <string>
  23. #import <type_traits>
  24. #import <vector>
  25. /**
  26. * Harness used for performance tests.
  27. *
  28. * The generator script will generate an extension to this class that adds a
  29. * run() method, which the main.swift file calls.
  30. */
  31. class Harness {
  32. public:
  33. /**
  34. * Creates a new harness that writes visualization output to the given
  35. * output stream.
  36. */
  37. Harness(std::ostream* results_stream);
  38. /**
  39. * Runs the test harness. This function is generated by the script into
  40. * _generated/Harness+Generated.cc.
  41. */
  42. void run();
  43. private:
  44. /** Microseconds unit for representing times. */
  45. typedef std::chrono::duration<double, std::micro> microseconds_d;
  46. /**
  47. * Statistics representing the mean and standard deviation of all measured
  48. * attempts.
  49. */
  50. struct Statistics {
  51. double mean;
  52. double stddev;
  53. };
  54. /** The output stream to which visualization results will be written. */
  55. std::ostream* results_stream;
  56. /**
  57. * The number of times to loop the body of the run() method.
  58. * Increase this for better precision.
  59. */
  60. int run_count() const;
  61. /** The number of times to measure the function passed to measure(). */
  62. int measurement_count;
  63. /** The number of times to add values to repeated fields. */
  64. int repeated_count;
  65. /** Ordered list of task names */
  66. std::vector<std::string> subtask_names;
  67. /** The times taken by subtasks during each measured attempt. */
  68. std::map<std::string, std::vector<microseconds_d>> subtask_timings;
  69. /** Times for the subtasks in the current attempt. */
  70. std::map<std::string, std::chrono::steady_clock::duration> current_subtasks;
  71. /**
  72. * Measures the time it takes to execute the given function. The function is
  73. * executed five times and the mean/standard deviation are computed.
  74. */
  75. template <typename Function>
  76. void measure(const Function& func);
  77. /**
  78. * Measure an individual subtask whose timing will be printed separately
  79. * from the main results.
  80. */
  81. template <typename Function>
  82. typename std::invoke_result<Function>::type measure_subtask(
  83. const std::string& name, Function&& func);
  84. /**
  85. * Writes the given subtask's name and timings to the visualization log.
  86. */
  87. void write_to_log(const std::string& name,
  88. const std::vector<microseconds_d>& timings) const;
  89. /**
  90. * Compute the mean and standard deviation of the given time points.
  91. */
  92. Statistics compute_statistics(
  93. const std::vector<std::chrono::steady_clock::duration>& timings) const;
  94. };
  95. template <typename Function>
  96. void Harness::measure(const Function& func) {
  97. using std::chrono::duration_cast;
  98. using std::chrono::steady_clock;
  99. using std::vector;
  100. vector<steady_clock::duration> timings;
  101. subtask_timings.clear();
  102. bool displayed_titles = false;
  103. printf("Running each check %d times, times in µs\n", run_count());
  104. // Do each measurement multiple times and collect the means and standard
  105. // deviation to account for noise.
  106. for (int attempt = 1; attempt <= measurement_count; attempt++) {
  107. current_subtasks.clear();
  108. auto start = steady_clock::now();
  109. for (auto i = 0; i < run_count(); i++) {
  110. subtask_names.clear();
  111. func();
  112. }
  113. auto end = steady_clock::now();
  114. auto duration = end - start;
  115. timings.push_back(duration);
  116. if (!displayed_titles) {
  117. auto names = std::vector<std::string>(subtask_names);
  118. printf("%3s", "");
  119. for (int i = 0; i < names.size(); i += 2) {
  120. printf("%-18s", names[i].c_str());
  121. }
  122. printf("\n");
  123. printf("%3s", "");
  124. printf("%9s", "");
  125. for (int i = 1; i < names.size(); i += 2) {
  126. printf("%-18s", names[i].c_str());
  127. }
  128. printf("\n");
  129. displayed_titles = true;
  130. }
  131. printf("%3d", attempt);
  132. for (const auto& name : subtask_names) {
  133. const auto& total_interval = current_subtasks[name];
  134. auto micros = duration_cast<microseconds_d>(total_interval);
  135. printf("%9.3f", micros.count() / run_count());
  136. subtask_timings[name].push_back(micros);
  137. }
  138. printf("\n");
  139. }
  140. for (const auto& entry : subtask_timings) {
  141. write_to_log(entry.first, entry.second);
  142. }
  143. auto stats = compute_statistics(timings);
  144. printf("Relative stddev = %.1f%%\n", stats.stddev / stats.mean * 100.0);
  145. }
  146. template <typename Function>
  147. typename std::invoke_result<Function>::type Harness::measure_subtask(
  148. const std::string& name, Function&& func) {
  149. subtask_names.push_back(name);
  150. using std::chrono::steady_clock;
  151. auto start = steady_clock::now();
  152. auto result = func();
  153. auto end = steady_clock::now();
  154. auto diff = end - start;
  155. current_subtasks[name] += diff;
  156. return result;
  157. }
  158. #endif // HARNESS_H