This repository was archived by the owner on Sep 29, 2020. It is now read-only.
forked from kohana/codebench
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Geert De Deckere
committed
Jun 20, 2009
0 parents
commit 35bc405
Showing
10 changed files
with
731 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?php defined('SYSPATH') OR die('No direct access allowed.'); | ||
/** | ||
* @author Geert De Deckere <[email protected]> | ||
*/ | ||
class Arrcallback extends Codebench { | ||
|
||
public $description = | ||
'Parsing "command[param,param]" strings in arr::callback(): | ||
http://github.com/shadowhand/kohana/commit/c3aaae849164bf92a486e29e736a265b350cb4da#L0R127'; | ||
|
||
public $loops = 10000; | ||
|
||
public $subjects = array | ||
( | ||
// Valid callback strings | ||
'foo', | ||
'foo::bar', | ||
'foo[apple,orange]', | ||
'foo::bar[apple,orange]', | ||
'[apple,orange]', // no command, only params | ||
'foo[[apple],[orange]]', // params with brackets inside | ||
|
||
// Invalid callback strings | ||
'foo[apple,orange', // no closing bracket | ||
); | ||
|
||
public function bench_shadowhand($subject) | ||
{ | ||
// The original regex we're trying to optimize | ||
if (preg_match('/([^\[]*+)\[(.*)\]/', $subject, $match)) | ||
return $match; | ||
} | ||
|
||
public function bench_geert_regex_1($subject) | ||
{ | ||
// Added ^ and $ around the whole pattern | ||
if (preg_match('/^([^\[]*+)\[(.*)\]$/', $subject, $matches)) | ||
return $matches; | ||
} | ||
|
||
public function bench_geert_regex_2($subject) | ||
{ | ||
// A rather experimental approach using \K which requires PCRE 7.2 ~ PHP 5.2.4 | ||
// Note: $matches[0] = params, $matches[1] = command | ||
if (preg_match('/^([^\[]*+)\[\K.*(?=\]$)/', $subject, $matches)) | ||
return $matches; | ||
} | ||
|
||
public function bench_geert_str($subject) | ||
{ | ||
// A native string function approach which beats all the regexes | ||
if (strpos($subject, '[') !== FALSE AND substr($subject, -1) === ']') | ||
return explode('[', substr($subject, 0, -1), 2); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<?php defined('SYSPATH') or die('No direct script access.'); | ||
|
||
class Codebench extends Kohana_Codebench {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php defined('SYSPATH') OR die('No direct access allowed.'); | ||
/** | ||
* Codebench — A benchmarking module. | ||
* | ||
* @package Kohana | ||
* @author Kohana Team | ||
* @copyright (c) 2009 Kohana Team | ||
* @license http://kohanaphp.com/license.html | ||
*/ | ||
class Controller_Codebench extends Kohana_Controller_Template { | ||
|
||
// The codebench view | ||
public $template = 'codebench'; | ||
|
||
public function action_index($class) | ||
{ | ||
// Convert submitted class name to URI segment | ||
if (isset($_POST['class'])) | ||
$this->request->redirect('codebench/'.strtolower(trim($_POST['class']))); | ||
|
||
// Pass the class name on to the view | ||
$this->template->class = $class = ucfirst(trim($class)); | ||
|
||
// Try to load the class, then run it | ||
if (Kohana::auto_load($class) === TRUE) | ||
{ | ||
$codebench = new $class; | ||
$this->template->codebench = $codebench->run(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
<?php defined('SYSPATH') OR die('No direct access allowed.'); | ||
/** | ||
* Codebench — A benchmarking module. | ||
* | ||
* @package Kohana | ||
* @author Kohana Team | ||
* @copyright (c) 2009 Kohana Team | ||
* @license http://kohanaphp.com/license.html | ||
*/ | ||
abstract class Kohana_Codebench { | ||
|
||
/** | ||
* @var string Some optional explanatory comments about the benchmark file. | ||
* HTML allowed. URLs will be converted to links automatically. | ||
*/ | ||
public $description = ''; | ||
|
||
/** | ||
* @var integer How many times to execute each method per subject. | ||
*/ | ||
public $loops = 1000; | ||
|
||
/** | ||
* @var array The subjects to supply iteratively to your benchmark methods. | ||
*/ | ||
public $subjects = array(); | ||
|
||
/** | ||
* @var array Grade letters with their maximum scores. Used to color the graphs. | ||
*/ | ||
public $grades = array | ||
( | ||
125 => 'A', | ||
150 => 'B', | ||
200 => 'C', | ||
300 => 'D', | ||
500 => 'E', | ||
'default' => 'F', | ||
); | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @return void | ||
*/ | ||
public function __construct() | ||
{ | ||
// Set the maximum execution time | ||
set_time_limit(Kohana::config('codebench')->max_execution_time); | ||
} | ||
|
||
/** | ||
* Runs Codebench on the extending class. | ||
* | ||
* @return array benchmark output | ||
*/ | ||
public function run() | ||
{ | ||
// Array of all methods to loop over | ||
$methods = array_filter(get_class_methods($this), array($this, 'method_filter')); | ||
|
||
// Make sure the benchmark runs at least once, | ||
// also if no subject data has been provided. | ||
if (empty($this->subjects)) | ||
{ | ||
$this->subjects = array('NULL' => NULL); | ||
} | ||
|
||
// Initialize benchmark output | ||
$codebench = array | ||
( | ||
'class' => get_class($this), | ||
'description' => $this->description, | ||
'loops' => array | ||
( | ||
'base' => (int) $this->loops, | ||
'total' => (int) $this->loops * count($this->subjects) * count($methods), | ||
), | ||
'subjects' => $this->subjects, | ||
'benchmarks' => array(), | ||
); | ||
|
||
// Benchmark each method | ||
foreach ($methods as $method) | ||
{ | ||
// Initialize benchmark output for this method | ||
$codebench['benchmarks'][$method] = array('time' => 0, 'memory' => 0); | ||
|
||
// Using Reflection because simply calling $this->$method($subject) in the loop below | ||
// results in buggy benchmark times correlating to the length of the method name. | ||
$reflection = new ReflectionMethod(get_class($this), $method); | ||
|
||
// Benchmark each subject on each method | ||
foreach ($this->subjects as $subject_key => $subject) | ||
{ | ||
// Prerun each method/subject combo before the actual benchmark loop. | ||
// This way relatively expensive initial processes won't be benchmarked, e.g. autoloading. | ||
// At the same time we capture the return here so we don't have to do that in the loop anymore. | ||
$return = $reflection->invoke($this, $subject); | ||
|
||
// Start the timer for one subject | ||
$token = Profiler::start('codebench', $method.$subject_key); | ||
|
||
// The heavy work | ||
for ($i = 0; $i < $this->loops; ++$i) | ||
{ | ||
$reflection->invoke($this, $subject); | ||
} | ||
|
||
// Stop and read the timer | ||
Profiler::stop($token); | ||
$benchmark = Profiler::total($token); | ||
|
||
// Temporary ugly workaround | ||
$benchmark['time'] = $benchmark[0]; | ||
$benchmark['memory'] = $benchmark[1]; | ||
|
||
// Benchmark output specific to the current method and subject | ||
$codebench['benchmarks'][$method]['subjects'][$subject_key] = array | ||
( | ||
'return' => $return, | ||
'time' => $benchmark['time'], | ||
'memory' => $benchmark['memory'], | ||
); | ||
|
||
// Update method totals | ||
$codebench['benchmarks'][$method]['time'] += $benchmark['time']; | ||
$codebench['benchmarks'][$method]['memory'] += $benchmark['memory']; | ||
} | ||
} | ||
|
||
// Initialize the fastest and slowest benchmarks for both methods and subjects, time and memory, | ||
// these values will be overwritten using min() and max() later on. | ||
// The 999999999 values look like a hack, I know, but they work, | ||
// unless your method runs for more than 31 years or consumes over 1GB of memory. | ||
$fastest_method = $fastest_subject = array('time' => 999999999, 'memory' => 999999999); | ||
$slowest_method = $slowest_subject = array('time' => 0, 'memory' => 0); | ||
|
||
// Find the fastest and slowest benchmarks, needed for the percentage calculations | ||
foreach ($methods as $method) | ||
{ | ||
// Update the fastest and slowest method benchmarks | ||
$fastest_method['time'] = min($fastest_method['time'], $codebench['benchmarks'][$method]['time']); | ||
$fastest_method['memory'] = min($fastest_method['memory'], $codebench['benchmarks'][$method]['memory']); | ||
$slowest_method['time'] = max($slowest_method['time'], $codebench['benchmarks'][$method]['time']); | ||
$slowest_method['memory'] = max($slowest_method['memory'], $codebench['benchmarks'][$method]['memory']); | ||
|
||
foreach ($this->subjects as $subject_key => $subject) | ||
{ | ||
// Update the fastest and slowest subject benchmarks | ||
$fastest_subject['time'] = min($fastest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']); | ||
$fastest_subject['memory'] = min($fastest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']); | ||
$slowest_subject['time'] = max($slowest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']); | ||
$slowest_subject['memory'] = max($slowest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']); | ||
} | ||
} | ||
|
||
// Percentage calculations for methods | ||
foreach ($codebench['benchmarks'] as & $method) | ||
{ | ||
// Calculate percentage difference relative to fastest and slowest methods | ||
$method['percent']['fastest']['time'] = $method['time'] / $fastest_method['time'] * 100; | ||
$method['percent']['fastest']['memory'] = $method['memory'] / $fastest_method['memory'] * 100; | ||
$method['percent']['slowest']['time'] = $method['time'] / $slowest_method['time'] * 100; | ||
$method['percent']['slowest']['memory'] = $method['memory'] / $slowest_method['memory'] * 100; | ||
|
||
// Assign a grade for time and memory to each method | ||
$method['grade']['time'] = $this->grade($method['percent']['fastest']['time']); | ||
$method['grade']['memory'] = $this->grade($method['percent']['fastest']['memory']); | ||
|
||
// Percentage calculations for subjects | ||
foreach ($method['subjects'] as & $subject) | ||
{ | ||
// Calculate percentage difference relative to fastest and slowest subjects for this method | ||
$subject['percent']['fastest']['time'] = $subject['time'] / $fastest_subject['time'] * 100; | ||
$subject['percent']['fastest']['memory'] = $subject['memory'] / $fastest_subject['memory'] * 100; | ||
$subject['percent']['slowest']['time'] = $subject['time'] / $slowest_subject['time'] * 100; | ||
$subject['percent']['slowest']['memory'] = $subject['memory'] / $slowest_subject['memory'] * 100; | ||
|
||
// Assign a grade letter for time and memory to each subject | ||
$subject['grade']['time'] = $this->grade($subject['percent']['fastest']['time']); | ||
$subject['grade']['memory'] = $this->grade($subject['percent']['fastest']['memory']); | ||
} | ||
} | ||
|
||
return $codebench; | ||
} | ||
|
||
/** | ||
* Callback for array_filter(). | ||
* Filters out all methods not to benchmark. | ||
* | ||
* @param string method name | ||
* @return boolean | ||
*/ | ||
protected function method_filter($method) | ||
{ | ||
// Only benchmark methods with the "bench" prefix | ||
return (substr($method, 0, 5) === 'bench'); | ||
} | ||
|
||
/** | ||
* Returns the applicable grade letter for a score. | ||
* | ||
* @param integer|double score | ||
* @return string grade letter | ||
*/ | ||
protected function grade($score) | ||
{ | ||
foreach ($this->grades as $max => $grade) | ||
{ | ||
if ($max === 'default') | ||
continue; | ||
|
||
if ($score <= $max) | ||
return $grade; | ||
} | ||
|
||
return $this->grades['default']; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php defined('SYSPATH') OR die('No direct access allowed.'); | ||
/** | ||
* @author Geert De Deckere <[email protected]> | ||
*/ | ||
class Ltrimdigits extends Codebench { | ||
|
||
public $description = 'Chopping off leading digits: regex vs ltrim.'; | ||
|
||
public $loops = 100000; | ||
|
||
public $subjects = array | ||
( | ||
'123digits', | ||
'no-digits', | ||
); | ||
|
||
public function bench_regex($subject) | ||
{ | ||
return preg_replace('/^\d+/', '', $subject); | ||
} | ||
|
||
public function bench_ltrim($subject) | ||
{ | ||
return ltrim($subject, '0..9'); | ||
} | ||
} |
Oops, something went wrong.