Skip to content
This repository was archived by the owner on Sep 29, 2020. It is now read-only.

Commit

Permalink
Porting Codebench to Kohana 3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Geert De Deckere committed Jun 20, 2009
0 parents commit 35bc405
Show file tree
Hide file tree
Showing 10 changed files with 731 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
55 changes: 55 additions & 0 deletions classes/arrcallback.php
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);
}
}
3 changes: 3 additions & 0 deletions classes/codebench.php
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 {}
31 changes: 31 additions & 0 deletions classes/controller/codebench.php
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();
}
}
}
221 changes: 221 additions & 0 deletions classes/kohana/codebench.php
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'];
}
}
26 changes: 26 additions & 0 deletions classes/ltrimdigits.php
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');
}
}
Loading

0 comments on commit 35bc405

Please sign in to comment.