3v4l.org

run code in 300+ PHP versions simultaneously
<?php namespace Lavoiesl\PhpBenchmark; class Profiler { private $start_memory = 0; private $max_memory = 0; private $start_time = null; private $end_time = null; public function start() { $this->start_memory = $this->max_memory = memory_get_usage(true); $this->start_time = microtime(true); register_tick_function( array( $this, "tick" ) ); } public function tick() { $this->max_memory = max($this->max_memory, memory_get_usage(true)); } public function stop() { $this->tick(); $this->end_time = microtime(true); unregister_tick_function( array( $this, "tick" ) ); } function getMemoryUsage() { return $this->max_memory - $this->start_memory; } function getTime() { return $this->end_time - $this->start_time; } } abstract class AbstractTest { /** * @var string */ private $name; private $profiler; public function __construct($name) { $this->name = $name; $this->profiler = new Profiler; } public function getName() { return $this->name; } public function run($n = 1) { $this->prepare(); gc_collect_cycles(); // clear memory before start $this->profiler->start(); for ($i=0; $i < $n; $i++) { // Store the result so it appears in memory profiling $result = $this->execute(); unset($result); } $this->profiler->stop(); $results = array( 'time' => $this->profiler->getTime(), 'memory' => $this->profiler->getMemoryUsage(), 'n' => $n, ); $this->cleanup(); return $results; } protected function prepare() { } abstract protected function execute(); protected function cleanup() { } public function guessCount($max_seconds = 1) { $this->run(); // warmup $once = $this->run(); if ($once['time'] >= $max_seconds) { return 1; } else { return round($max_seconds / $once['time']); } } } class SimpleTest extends AbstractTest { /** * @var \Closure */ private $prepare = null; /** * @var \Closure */ private $execute; /** * @var \Closure */ private $cleanup = null; public function __construct($name, \Closure $execute) { parent::__construct($name); $this->execute = $execute; } public function setPrepare(\Closure $prepare) { $this->prepare = $prepare; return $this; } protected function prepare() { if ($prepare = $this->prepare) { $prepare(); } } protected function execute() { return call_user_func($this->execute); } public function setCleanup(\Closure $cleanup) { $this->cleanup = $cleanup; return $this; } protected function cleanup() { if ($cleanup = $this->cleanup) { $cleanup(); } } } class Benchmark { /** * @var array [Test] */ private $tests = array(); private $n = null; private $base_results; public function addTest(AbstractTest $test) { $this->tests[$test->getName()] = $test; } /** * Utility method to create tests on the fly * You may chain the test: * * @param string $name * @param \Closure $closure function to execute * @return SimpleTest */ public function add($name, \Closure $closure) { $test = new SimpleTest($name, $closure); $this->addTest($test); return $test; } /** * Runs an empty test to determine the benchmark overhead and run each test once */ private function warmup() { $warmup = new SimpleTest('warmup', function(){}); $warmup->run(); foreach ($this->tests as $test) { $test->run(); } $this->base_results = $warmup->run($this->n); } public function run($output = true) { $results = array(); if (null === $this->n) { $this->guessCount(2); // aim for around 2 seconds per test } if ($output) { echo "Running tests {$this->n} times.\n"; } $this->warmup(); $i = 0; foreach ($this->tests as $name => $test) { if ($output) { echo "Testing ".++$i."/".count($this->tests)." : $name\r"; } $results[$name] = $test->run($this->n); } if ($output) { echo "\n\n"; self::outputTable(self::formatResults($results)); } return $results; } public function setCount($n) { $this->n = $n; } /** * Average the guessCount of each test, determining the best n * * @param float $max_seconds * @return int */ public function guessCount($max_seconds) { if (!$this->tests) { throw new \RuntimeException('No test in Benchmark.'); } $n = INF; foreach ($this->tests as $test) { $n = min($n, $test->guessCount($max_seconds)); } return $this->n = Util::round($n); } /** * Output results in columns, padding right if values are string, left if numeric * * @param array $lines array(array('Name' => 'Value')); * @param integer $padding space between columns */ public static function outputTable(array $lines, $padding = 3) { if (!$lines) { return; } $pad = function ($string, $width) use ($padding) { if ($width > 0) { return str_pad($string, $width, " ") . str_repeat(' ' , $padding); } else { return str_pad($string, -$width, " ", STR_PAD_LEFT) . str_repeat(' ' , $padding); } }; // init width with keys' length $cols = array_combine(array_keys($lines[0]), array_map('strlen', array_keys($lines[0]))); foreach ($cols as $col => $width) { foreach ($lines as $line) { $width = max($width, strlen($line[$col])); } // pad left if numeric if (preg_match('/^[0-9]/', $line[$col])) { $width = -$width; } echo $pad($col, $width); $cols[$col] = $width; } echo "\n"; foreach ($lines as $line) { foreach ($cols as $col => $width) { echo $pad($line[$col], $width); } echo "\n"; } } /** * Format the results, rounding numbers, showing difference percentages * and removing a flat time based on the benchmark overhead * * @param array $results array($name => array('time' => 1.0)) * @return array array(array('Test' => $name, 'Time' => '1000 ms', 'Perc' => '100 %')) */ public function formatResults(array $results) { uasort($results, function($a, $b) { if ($a['time'] == $b['time']) return 0; else return ($a['time'] < $b['time']) ? -1 : 1; }); $min_time = INF; $min_memory = INF; foreach ($results as $name => $result) { $time = $result['time']; $time -= $this->base_results['time']; // Substract base_time $time *= 1000; // Convert to ms $time = round($time); $time = max(1, $time); // min 1 ms $min_time = min($min_time, $time); $results[$name]['time'] = $time; $min_memory = min($min_memory, $results[$name]['memory']); } $output = array(); foreach ($results as $name => $result) { $output[] = array( 'Test' => $name, 'Time' => $result['time'] . ' ms', 'Time (%)' => Util::relativePerc($min_time, $result['time']), 'Memory' => Util::convertToSI($result['memory']), 'Memory (%)' => Util::relativePerc($min_memory, $result['memory']), ); } return $output; } } class Util { public static function round($number, $significant = 0) { $order = floor(log($number) / log(10)); return round($number / pow(10, $order), $significant) * pow(10, $order); } /** * Converts 1024 to 1K, etc. * * @param double $number i.e.: 1280 * @param integer $precision i.e.: 1.25 for precision = 2 * @param string $unit suffix of the unit, may be empty * @param integer $factor change base to 1000 or 1024 * @return string i.e.: 1.25 kB */ public static function convertToSI($number, $precision = 2, $unit = 'B', $factor = 1024) { static $sizes = array( '-3' => 'n', '-2' => 'ยต', '-1' => 'm', '0' => '', '1' => 'k', '2' => 'M', '3' => 'G', '4' => 'T' ); $scale = $number == 0 ? 0 : floor(log($number, $factor)); return round($number / pow($factor, $scale), $precision) . ' ' . $sizes[$scale] . $unit = 'B'; } public static function relativePerc($min, $value) { if ($min == 0 || $min == $value) { return ''; } else { return round(($value - $min) / $min * 100) . ' %'; } } } $benchmark = new Benchmark; function multiexplode ($delimiters,$string) { $ready = str_replace($delimiters, $delimiters[0], $string); $launch = explode($delimiters[0], $ready); return $launch; } function homemadeexplode($splitter, $str) { $return = []; if (is_array($splitter) == true) { foreach ($splitter as $sp) { $str = homemadeexplode($sp, $str); } $return = $str; } else { if (is_array($str) == true) { foreach ($str as $st) { $tmp = explode($splitter, $st); $return = array_merge($return, $tmp); } } else { $return = explode($splitter, $str); } } return $return; } $text = "here is a sample: this text, and this will be exploded. this also | this one too :)"; $benchmark->add('multiexplode', function() use($text) { return multiexplode( [",",".","|",":"], $text); }); $benchmark->add('homemadeExplode', function() use($text) { return homemadeexplode([",",".","|",":"], $text); }); $benchmark->add('preg_split', function() use($text) { return preg_split( '/[,.|:]/', $text); }); $benchmark->add('mb_split', function() use($text) { return mb_split( '[,.|:]', $text); }); $benchmark->setCount(50000); $benchmark->run();

preferences:
38.41 ms | 402 KiB | 5 Q