PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/codebench/classes/kohana/codebench.php

https://bitbucket.org/levjke/kohanablogds
PHP | 217 lines | 112 code | 28 blank | 77 comment | 4 complexity | dfae8afb9dc901068c152622ab0f0d1a MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. <?php defined('SYSPATH') or die('No direct access allowed.');
  2. /**
  3. * Codebench — A benchmarking module.
  4. *
  5. * @package Kohana/Codebench
  6. * @category Base
  7. * @author Kohana Team
  8. * @copyright (c) 2009 Kohana Team
  9. * @license http://kohanaphp.com/license.html
  10. */
  11. abstract class Kohana_Codebench {
  12. /**
  13. * @var string Some optional explanatory comments about the benchmark file.
  14. * HTML allowed. URLs will be converted to links automatically.
  15. */
  16. public $description = '';
  17. /**
  18. * @var integer How many times to execute each method per subject.
  19. */
  20. public $loops = 1000;
  21. /**
  22. * @var array The subjects to supply iteratively to your benchmark methods.
  23. */
  24. public $subjects = array();
  25. /**
  26. * @var array Grade letters with their maximum scores. Used to color the graphs.
  27. */
  28. public $grades = array
  29. (
  30. 125 => 'A',
  31. 150 => 'B',
  32. 200 => 'C',
  33. 300 => 'D',
  34. 500 => 'E',
  35. 'default' => 'F',
  36. );
  37. /**
  38. * Constructor.
  39. *
  40. * @return void
  41. */
  42. public function __construct()
  43. {
  44. // Set the maximum execution time
  45. set_time_limit(Kohana::$config->load('codebench')->max_execution_time);
  46. }
  47. /**
  48. * Runs Codebench on the extending class.
  49. *
  50. * @return array benchmark output
  51. */
  52. public function run()
  53. {
  54. // Array of all methods to loop over
  55. $methods = array_filter(get_class_methods($this), array($this, '_method_filter'));
  56. // Make sure the benchmark runs at least once,
  57. // also if no subject data has been provided.
  58. if (empty($this->subjects))
  59. {
  60. $this->subjects = array('NULL' => NULL);
  61. }
  62. // Initialize benchmark output
  63. $codebench = array
  64. (
  65. 'class' => get_class($this),
  66. 'description' => $this->description,
  67. 'loops' => array
  68. (
  69. 'base' => (int) $this->loops,
  70. 'total' => (int) $this->loops * count($this->subjects) * count($methods),
  71. ),
  72. 'subjects' => $this->subjects,
  73. 'benchmarks' => array(),
  74. );
  75. // Benchmark each method
  76. foreach ($methods as $method)
  77. {
  78. // Initialize benchmark output for this method
  79. $codebench['benchmarks'][$method] = array('time' => 0, 'memory' => 0);
  80. // Using Reflection because simply calling $this->$method($subject) in the loop below
  81. // results in buggy benchmark times correlating to the length of the method name.
  82. $reflection = new ReflectionMethod(get_class($this), $method);
  83. // Benchmark each subject on each method
  84. foreach ($this->subjects as $subject_key => $subject)
  85. {
  86. // Prerun each method/subject combo before the actual benchmark loop.
  87. // This way relatively expensive initial processes won't be benchmarked, e.g. autoloading.
  88. // At the same time we capture the return here so we don't have to do that in the loop anymore.
  89. $return = $reflection->invoke($this, $subject);
  90. // Start the timer for one subject
  91. $token = Profiler::start('codebench', $method.$subject_key);
  92. // The heavy work
  93. for ($i = 0; $i < $this->loops; ++$i)
  94. {
  95. $reflection->invoke($this, $subject);
  96. }
  97. // Stop and read the timer
  98. $benchmark = Profiler::total($token);
  99. // Benchmark output specific to the current method and subject
  100. $codebench['benchmarks'][$method]['subjects'][$subject_key] = array
  101. (
  102. 'return' => $return,
  103. 'time' => $benchmark[0],
  104. 'memory' => $benchmark[1],
  105. );
  106. // Update method totals
  107. $codebench['benchmarks'][$method]['time'] += $benchmark[0];
  108. $codebench['benchmarks'][$method]['memory'] += $benchmark[1];
  109. }
  110. }
  111. // Initialize the fastest and slowest benchmarks for both methods and subjects, time and memory,
  112. // these values will be overwritten using min() and max() later on.
  113. // The 999999999 values look like a hack, I know, but they work,
  114. // unless your method runs for more than 31 years or consumes over 1GB of memory.
  115. $fastest_method = $fastest_subject = array('time' => 999999999, 'memory' => 999999999);
  116. $slowest_method = $slowest_subject = array('time' => 0, 'memory' => 0);
  117. // Find the fastest and slowest benchmarks, needed for the percentage calculations
  118. foreach ($methods as $method)
  119. {
  120. // Update the fastest and slowest method benchmarks
  121. $fastest_method['time'] = min($fastest_method['time'], $codebench['benchmarks'][$method]['time']);
  122. $fastest_method['memory'] = min($fastest_method['memory'], $codebench['benchmarks'][$method]['memory']);
  123. $slowest_method['time'] = max($slowest_method['time'], $codebench['benchmarks'][$method]['time']);
  124. $slowest_method['memory'] = max($slowest_method['memory'], $codebench['benchmarks'][$method]['memory']);
  125. foreach ($this->subjects as $subject_key => $subject)
  126. {
  127. // Update the fastest and slowest subject benchmarks
  128. $fastest_subject['time'] = min($fastest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']);
  129. $fastest_subject['memory'] = min($fastest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']);
  130. $slowest_subject['time'] = max($slowest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']);
  131. $slowest_subject['memory'] = max($slowest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']);
  132. }
  133. }
  134. // Percentage calculations for methods
  135. foreach ($codebench['benchmarks'] as & $method)
  136. {
  137. // Calculate percentage difference relative to fastest and slowest methods
  138. $method['percent']['fastest']['time'] = (empty($fastest_method['time'])) ? 0 : ($method['time'] / $fastest_method['time'] * 100);
  139. $method['percent']['fastest']['memory'] = (empty($fastest_method['memory'])) ? 0 : ($method['memory'] / $fastest_method['memory'] * 100);
  140. $method['percent']['slowest']['time'] = (empty($slowest_method['time'])) ? 0 : ($method['time'] / $slowest_method['time'] * 100);
  141. $method['percent']['slowest']['memory'] = (empty($slowest_method['memory'])) ? 0 : ($method['memory'] / $slowest_method['memory'] * 100);
  142. // Assign a grade for time and memory to each method
  143. $method['grade']['time'] = $this->_grade($method['percent']['fastest']['time']);
  144. $method['grade']['memory'] = $this->_grade($method['percent']['fastest']['memory']);
  145. // Percentage calculations for subjects
  146. foreach ($method['subjects'] as & $subject)
  147. {
  148. // Calculate percentage difference relative to fastest and slowest subjects for this method
  149. $subject['percent']['fastest']['time'] = (empty($fastest_subject['time'])) ? 0 : ($subject['time'] / $fastest_subject['time'] * 100);
  150. $subject['percent']['fastest']['memory'] = (empty($fastest_subject['memory'])) ? 0 : ($subject['memory'] / $fastest_subject['memory'] * 100);
  151. $subject['percent']['slowest']['time'] = (empty($slowest_subject['time'])) ? 0 : ($subject['time'] / $slowest_subject['time'] * 100);
  152. $subject['percent']['slowest']['memory'] = (empty($slowest_subject['memory'])) ? 0 : ($subject['memory'] / $slowest_subject['memory'] * 100);
  153. // Assign a grade letter for time and memory to each subject
  154. $subject['grade']['time'] = $this->_grade($subject['percent']['fastest']['time']);
  155. $subject['grade']['memory'] = $this->_grade($subject['percent']['fastest']['memory']);
  156. }
  157. }
  158. return $codebench;
  159. }
  160. /**
  161. * Callback for array_filter().
  162. * Filters out all methods not to benchmark.
  163. *
  164. * @param string method name
  165. * @return boolean
  166. */
  167. protected function _method_filter($method)
  168. {
  169. // Only benchmark methods with the "bench" prefix
  170. return (substr($method, 0, 5) === 'bench');
  171. }
  172. /**
  173. * Returns the applicable grade letter for a score.
  174. *
  175. * @param integer|double score
  176. * @return string grade letter
  177. */
  178. protected function _grade($score)
  179. {
  180. foreach ($this->grades as $max => $grade)
  181. {
  182. if ($max === 'default')
  183. continue;
  184. if ($score <= $max)
  185. return $grade;
  186. }
  187. return $this->grades['default'];
  188. }
  189. }