Prestandatester av PHP-funktioner
Det händer ibland att jag känner mig osäker på vilket tillvägagångssätt jag ska välja för att uppnå maximal prestanda. Jag vänder mig då till google som, efter ett inte allt för tidskrävande sökande, ger mig svaret på mina frågor. Men vad göra när svaren inte går att hitta?
Sedan en tid tillbaka har jag börjat skriva mina egna tester istället för att använda mig av google. Ibland råder det delade meningar om vilken funktion, eller tillvägagångssätt, som är att föredra och då kan man med fördel själv skriva ett test som bevisar vad som faktiskt ger bäst prestanda. Jag tror även att man lär sig ett och annat genom att själv utforma testerna. Jag har skrivit en abstrakt klass som jag använder mig av för att enkelt sätta upp nya tester. Min senaste testrunda visade följande resultat:

AlgorithmComparison
abstract class AlgorithmComparison {
/**
* Description of function A
* @var string
*/
private $test_a_name;
/**
* Description of function B
* @var string
*/
private $test_b_name;
/**
* @param string $test_a_name
* @param string $test_b_name
*/
public function __construct($test_a_name, $test_b_name) {
$this->test_a_name = $test_a_name;
$this->test_b_name = $test_b_name;
}
/**
* @param bool $html_format[optional=true]
* @param int $num_runs[optional=50000]
* @return string|array
*/
public function runTest($html_format = true, $num_runs = 50000) {
$result_a = $this->runTestFunction('testA', $this->test_a_name, $num_runs);
$result_b = $this->runTestFunction('testB', $this->test_b_name, $num_runs);
return $html_format ?
$this->formatTestResult($result_a, $result_b) :
array($result_a, $result_b);
}
/**
* @param string $func_name
* @param string $desc
* @param int $num_runs[optional=50000]
* @return array
*/
private function runTestFunction($func_name, $desc, $num_runs = 50000) {
$start_mem_usage = memory_get_usage();
$start_time = $this->getMicroTime();
for($i=$num_runs; $i >0; $i--)
$this->$func_name();
$memory = memory_get_usage() - $start_mem_usage;
return array(
'function' => $func_name,
'description' => $desc,
'mem_usage' => round($memory/1024/1024, 4),
'time' => $this->getMicroTime() - $start_time
);
}
/**
* @see http://se2.php.net/manual/en/function.microtime.php#101875
* @return float
*/
private function getMicroTime() {
list($u, $s) = explode(' ',microtime(false));
return bcadd($u, $s, 7);
}
/**
* @param array $test_result_a
* @param array $test_result_b
* @return string
*/
private function formatTestResult($test_result_a, $test_result_b) {
$html = '
<table border="1" cellspacing="2" cellpadding="4">
<thead>
<tr>
<td><strong>Function description</strong></td>
<td><strong>Execution time (ms)</strong></td>
<td><strong>Memory usage (mb)</strong></td>
</tr>
</thead>
<tbody>
<tr>
<td>%s</td>
<td>%f</td>
<td>%f</td>
</tr>
<tr>
<td>%s</td>
<td>%f</td>
<td>%f</td>
</tr>
</tbody>
</table>';
$winner = $test_result_a['time'] < $test_result_b['time'] ? $test_result_a : $test_result_b;
$loser = $winner['function'] == $test_result_a['function'] ? $test_result_b : $test_result_a;
return sprintf(
$html,
$winner['description'],
$winner['time'],
$winner['mem_usage'],
$loser['description'],
$loser['time'],
$loser['mem_usage']
);
}
abstract function testA();
abstract function testB();
}
Exempel på implementation
class SomeArrayTest extends AlgorithmComparison {
public function __construct() {
parent::__construct('isset($array[\'my_key\'])', 'key_exists(\'my_key\')');
}
public function testA() {
$arr = array(2,4532,23,453,232,5534,'my_key'=>39284,234,23);
return isset($arr['my_key']);
}
public function testB() {
$arr = array(2,4532,23,453,232,5534,'my_key'=>39284,234,23);
return key_exists('my_key', $arr);
}
}
$test = new SomeArrayTest();
echo $test->runTest();