├── .gitignore ├── benchutils ├── testFunctions.php ├── helpers.php └── benchFunctions.php ├── composer.json ├── src ├── InvokeFactory.php ├── InvokeInterface.php ├── InvokeClosure.php ├── ClosureFactory.php ├── InvokeFunction.php ├── InvokeInstance.php ├── InvokeStatic.php └── InvokeCallUserFunc.php ├── .gitattributes ├── bench └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.lock -------------------------------------------------------------------------------- /benchutils/testFunctions.php: -------------------------------------------------------------------------------- 1 | $str" : "$plus$str"; 6 | } 7 | 8 | function calcVs(&$reportBench, $compare) 9 | { 10 | $diff = $reportBench[$compare]['time'] - $reportBench['call_user_func']['time']; 11 | $reportBench[$compare]['diff'] = $diff; 12 | $reportBench[$compare]['pct'] = $diff * 100 / $reportBench['call_user_func']['time']; 13 | } 14 | 15 | function sortReportByDiff($a, $b) 16 | { 17 | global $report, $key; 18 | return $report[$key][$a]["diff"] >= $report[$key][$b]["diff"]; 19 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "fab2s/call_user_func", 3 | "description" : "A benchmark study of call_user_func()", 4 | "type" : "library", 5 | "authors" : [{ 6 | "name" : "Fabrice de Stefanis" 7 | }], 8 | "keywords" : [ 9 | "call_user_func call_user_func_array study benchmark" 10 | ], 11 | "license" : [ 12 | "MIT" 13 | ], 14 | "require" : { 15 | "php": ">=5.3.0" 16 | }, 17 | "require-dev": { 18 | "phpunit/php-timer": "^1.0", 19 | "symfony/console": "^4.0|^3.4" 20 | }, 21 | "autoload": { 22 | "classmap": [ 23 | "src" 24 | ] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/InvokeFactory.php: -------------------------------------------------------------------------------- 1 | closure = $closure; 24 | } 25 | 26 | /** 27 | * 28 | * @return mixed 29 | */ 30 | public function exec() 31 | { 32 | $closure = $this->closure; 33 | return $closure(); 34 | } 35 | 36 | /** 37 | * 38 | * @return mixed 39 | */ 40 | public function execOneArg($param) 41 | { 42 | $closure = $this->closure; 43 | return $closure($param); 44 | } 45 | 46 | /** 47 | * 48 | * @return mixed 49 | */ 50 | public function execTwoArg($param1, $param2) 51 | { 52 | $closure = $this->closure; 53 | return $closure($param1, $param2); 54 | } 55 | 56 | /** 57 | * 58 | * @return mixed 59 | */ 60 | public function execThreeArg($param1, $param2, $param3) 61 | { 62 | $closure = $this->closure; 63 | return $closure($param1, $param2, $param3); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/ClosureFactory.php: -------------------------------------------------------------------------------- 1 | $method($param); 36 | }; 37 | } else { 38 | return function ($param) use ($callable) { 39 | return $callable($param); 40 | }; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/InvokeFunction.php: -------------------------------------------------------------------------------- 1 | function = $function; 24 | } 25 | 26 | /** 27 | * 28 | * @return mixed 29 | */ 30 | public function exec() 31 | { 32 | $function = $this->function; 33 | return $function(); 34 | } 35 | 36 | /** 37 | * 38 | * @return mixed 39 | */ 40 | public function execOneArg($param) 41 | { 42 | $function = $this->function; 43 | return $function($param); 44 | } 45 | 46 | /** 47 | * 48 | * @return mixed 49 | */ 50 | public function execTwoArg($param1, $param2) 51 | { 52 | $function = $this->function; 53 | return $function($param1, $param2); 54 | } 55 | 56 | /** 57 | * 58 | * @return mixed 59 | */ 60 | public function execThreeArg($param1, $param2, $param3) 61 | { 62 | $function = $this->function; 63 | return $function($param1, $param2, $param3); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/InvokeInstance.php: -------------------------------------------------------------------------------- 1 | instance = $instance; 31 | $this->method = $method; 32 | } 33 | 34 | /** 35 | * 36 | * @return mixed 37 | */ 38 | public function exec() 39 | { 40 | return $this->instance->{$this->method}(); 41 | } 42 | 43 | /** 44 | * 45 | * @return mixed 46 | */ 47 | public function execOneArg($param) 48 | { 49 | return $this->instance->{$this->method}($param); 50 | } 51 | 52 | /** 53 | * 54 | * @return mixed 55 | */ 56 | public function execTwoArg($param1, $param2) 57 | { 58 | return $this->instance->{$this->method}($param1, $param2); 59 | } 60 | 61 | /** 62 | * 63 | * @return mixed 64 | */ 65 | public function execThreeArg($param1, $param2, $param3) 66 | { 67 | return $this->instance->{$this->method}($param1, $param2, $param3); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/InvokeStatic.php: -------------------------------------------------------------------------------- 1 | class = $class; 31 | $this->method = $method; 32 | } 33 | 34 | /** 35 | * 36 | * @return mixed 37 | */ 38 | public function exec() 39 | { 40 | $class = $this->class; 41 | return $class::{$this->method}(); 42 | } 43 | 44 | /** 45 | * 46 | * @return mixed 47 | */ 48 | public function execOneArg($param) 49 | { 50 | $class = $this->class; 51 | return $class::{$this->method}($param); 52 | } 53 | 54 | /** 55 | * 56 | * @return mixed 57 | */ 58 | public function execTwoArg($param1, $param2) 59 | { 60 | $class = $this->class; 61 | return $class::{$this->method}($param1, $param2); 62 | } 63 | 64 | /** 65 | * 66 | * @return mixed 67 | */ 68 | public function execThreeArg($param1, $param2, $param3) 69 | { 70 | $class = $this->class; 71 | return $class::{$this->method}($param1, $param2, $param3); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/InvokeCallUserFunc.php: -------------------------------------------------------------------------------- 1 | calable = $calable; 31 | } 32 | 33 | /** 34 | * 35 | * @return mixed 36 | */ 37 | public function exec() 38 | { 39 | return call_user_func($this->calable); 40 | } 41 | 42 | /** 43 | * 44 | * @param mixed $param 45 | * @return mixed 46 | */ 47 | public function execOneArg($param) 48 | { 49 | return call_user_func($this->calable, $param); 50 | } 51 | 52 | /** 53 | * 54 | * @return mixed 55 | */ 56 | public function execTwoArg($param1, $param2) 57 | { 58 | return call_user_func($this->calable, $param1, $param2); 59 | } 60 | 61 | /** 62 | * 63 | * @return mixed 64 | */ 65 | public function execThreeArg($param1, $param2, $param3) 66 | { 67 | return call_user_func($this->calable, $param1, $param2, $param3); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # laravel default 2 | *.css linguist-vendored 3 | *.less linguist-vendored 4 | 5 | # These settings are for any web project 6 | 7 | # Handle line endings automatically for files detected as text 8 | # and leave all files detected as binary untouched. 9 | * text=auto 10 | 11 | # 12 | # The above will handle all files NOT found below 13 | # 14 | 15 | # 16 | ## These files are text and should be normalized (Convert crlf => lf) 17 | # 18 | 19 | # source code 20 | *.php text 21 | *.css text 22 | *.sass text 23 | *.scss text 24 | *.less text 25 | *.styl text 26 | *.js text 27 | *.coffee text 28 | *.json text 29 | *.htm text 30 | *.html text 31 | *.xml text 32 | *.svg text 33 | *.txt text 34 | *.ini text 35 | *.inc text 36 | *.pl text 37 | *.rb text 38 | *.py text 39 | *.scm text 40 | *.sql text 41 | *.sh text 42 | *.bat text 43 | 44 | # templates 45 | *.ejs text 46 | *.hbt text 47 | *.jade text 48 | *.haml text 49 | *.hbs text 50 | *.dot text 51 | *.tmpl text 52 | *.phtml text 53 | 54 | # server config 55 | .htaccess text 56 | 57 | # git config 58 | .gitattributes text 59 | .gitignore text 60 | 61 | # code analysis config 62 | .jshintrc text 63 | .jscsrc text 64 | .jshintignore text 65 | .csslintrc text 66 | 67 | # misc config 68 | *.yaml text 69 | *.yml text 70 | .editorconfig text 71 | 72 | # build config 73 | *.npmignore text 74 | *.bowerrc text 75 | 76 | # Heroku 77 | Procfile text 78 | .slugignore text 79 | 80 | # Documentation 81 | *.md text 82 | LICENSE text 83 | AUTHORS text 84 | 85 | # 86 | ## These files are binary and should be left untouched 87 | # 88 | 89 | # (binary is a macro for -text -diff) 90 | *.png binary 91 | *.jpg binary 92 | *.jpeg binary 93 | *.gif binary 94 | *.ico binary 95 | *.mov binary 96 | *.mp4 binary 97 | *.mp3 binary 98 | *.flv binary 99 | *.fla binary 100 | *.swf binary 101 | *.gz binary 102 | *.zip binary 103 | *.7z binary 104 | *.ttf binary 105 | *.eot binary 106 | *.woff binary 107 | *.pyc binary 108 | *.pdf binary 109 | -------------------------------------------------------------------------------- /benchutils/benchFunctions.php: -------------------------------------------------------------------------------- 1 | methodTest($param); 33 | } 34 | $avgTime += PHP_Timer::stop(); 35 | } 36 | 37 | return $avgTime/$averageOver; 38 | } 39 | 40 | function benchDirectFunction($param) 41 | { 42 | $avgTime = 0; 43 | $averageOver = AVG_OVER; 44 | $iterations = ITERATIONS; 45 | for ($i = 0; $i < $averageOver; $i++) { 46 | PHP_Timer::start(); 47 | for ($cnt = 1; $cnt <= $iterations; $cnt++) { 48 | functionTest($param); 49 | } 50 | $avgTime += PHP_Timer::stop(); 51 | } 52 | 53 | return $avgTime/$averageOver; 54 | } 55 | 56 | function benchDirectLambda($param) 57 | { 58 | global $lambdaTest; 59 | $avgTime = 0; 60 | $averageOver = AVG_OVER; 61 | $iterations = ITERATIONS; 62 | for ($i = 0; $i < $averageOver; $i++) { 63 | PHP_Timer::start(); 64 | for ($cnt = 1; $cnt <= $iterations; $cnt++) { 65 | $lambdaTest($param); 66 | } 67 | $avgTime += PHP_Timer::stop(); 68 | } 69 | 70 | return $avgTime/$averageOver; 71 | } 72 | 73 | function benchDirectClosure($param) 74 | { 75 | global $closureTest; 76 | $avgTime = 0; 77 | $averageOver = AVG_OVER; 78 | $iterations = ITERATIONS; 79 | for ($i = 0; $i < $averageOver; $i++) { 80 | PHP_Timer::start(); 81 | for ($cnt = 1; $cnt <= $iterations; $cnt++) { 82 | $closureTest($param); 83 | } 84 | $avgTime += PHP_Timer::stop(); 85 | } 86 | 87 | return $avgTime/$averageOver; 88 | } 89 | 90 | function benchInvoke($callable, $param) 91 | { 92 | $Invoker = InvokeFactory::create($callable); 93 | $avgTime = 0; 94 | $averageOver = AVG_OVER; 95 | $iterations = ITERATIONS; 96 | for ($i = 0; $i < $averageOver; $i++) { 97 | PHP_Timer::start(); 98 | for ($cnt = 1; $cnt <= $iterations; $cnt++) { 99 | $Invoker->execOneArg($param); 100 | } 101 | $avgTime += PHP_Timer::stop(); 102 | } 103 | 104 | return $avgTime/$averageOver; 105 | } 106 | 107 | function benchCall_user_func($callable, $param) 108 | { 109 | $avgTime = 0; 110 | $averageOver = AVG_OVER; 111 | $iterations = ITERATIONS; 112 | for ($i = 0; $i < $averageOver; $i++) { 113 | PHP_Timer::start(); 114 | for ($cnt = 1; $cnt <= $iterations; $cnt++) { 115 | call_user_func($callable, $param); 116 | } 117 | $avgTime += PHP_Timer::stop(); 118 | } 119 | 120 | return $avgTime/$averageOver; 121 | } 122 | 123 | function benchCall_user_func_array($callable, $param) 124 | { 125 | $avgTime = 0; 126 | $averageOver = AVG_OVER; 127 | $iterations = ITERATIONS; 128 | $param = [$param]; 129 | for ($i = 0; $i < $averageOver; $i++) { 130 | PHP_Timer::start(); 131 | for ($cnt = 1; $cnt <= $iterations; $cnt++) { 132 | call_user_func_array($callable, $param); 133 | } 134 | $avgTime += PHP_Timer::stop(); 135 | } 136 | 137 | return $avgTime/$averageOver; 138 | } 139 | 140 | function benchAssignedClosureFactory($callable, $param) 141 | { 142 | $Invoker = ClosureFactory::create($callable); 143 | $avgTime = 0; 144 | $averageOver = AVG_OVER; 145 | $iterations = ITERATIONS; 146 | for ($i = 0; $i < $averageOver; $i++) { 147 | PHP_Timer::start(); 148 | for ($cnt = 1; $cnt <= $iterations; $cnt++) { 149 | $call = $Invoker; 150 | $call($param); 151 | } 152 | $avgTime += PHP_Timer::stop(); 153 | } 154 | 155 | return $avgTime/$averageOver; 156 | } 157 | 158 | function benchClosureFactory($callable, $param) 159 | { 160 | $Invoker = ClosureFactory::create($callable); 161 | $avgTime = 0; 162 | $averageOver = AVG_OVER; 163 | $iterations = ITERATIONS; 164 | for ($i = 0; $i < $averageOver; $i++) { 165 | PHP_Timer::start(); 166 | for ($cnt = 1; $cnt <= $iterations; $cnt++) { 167 | $Invoker($param); 168 | } 169 | $avgTime += PHP_Timer::stop(); 170 | } 171 | 172 | return $avgTime/$averageOver; 173 | } 174 | 175 | function benchInvokeCallUserFunc($callable, $param) 176 | { 177 | $invokeCallUserFunc = new InvokeCallUserFunc($callable); 178 | $avgTime = 0; 179 | $averageOver = AVG_OVER; 180 | $iterations = ITERATIONS; 181 | for ($i = 0; $i < $averageOver; $i++) { 182 | PHP_Timer::start(); 183 | for ($cnt = 1; $cnt <= $iterations; $cnt++) { 184 | $invokeCallUserFunc->execOneArg($param); 185 | } 186 | $avgTime += PHP_Timer::stop(); 187 | } 188 | 189 | return $avgTime/$averageOver; 190 | } 191 | 192 | function benchDirectImplementation($callable, $param) 193 | { 194 | $avgTime = 0; 195 | $averageOver = AVG_OVER; 196 | $iterations = ITERATIONS; 197 | for ($i = 0; $i < $averageOver; $i++) { 198 | PHP_Timer::start(); 199 | for ($cnt = 1; $cnt <= $iterations; $cnt++) { 200 | if (is_string($callable)) { 201 | $callable = trim($callable, '\\'); 202 | if (strpos($callable, '::')) { 203 | list($class, $method) = explode('::', $callable); 204 | $class::$method($param); 205 | } else { 206 | $callable($param); 207 | } 208 | } else if (is_array($callable)) { 209 | $class = $callable[0]; 210 | $method = $callable[1]; 211 | $class->{$method}($param); 212 | } else { 213 | $callable($param); 214 | } 215 | } 216 | $avgTime += PHP_Timer::stop(); 217 | } 218 | 219 | return $avgTime/$averageOver; 220 | } -------------------------------------------------------------------------------- /bench: -------------------------------------------------------------------------------- 1 | setFormatter(new OutputFormatter(true)); 16 | 17 | $iterations = 100000; 18 | $averageOver = 10; 19 | if (!empty($argv[1]) && ($argv[1] === '--help' || $argv[1] === '-h')) { 20 | $output->writeln("bench usage 21 | no options : run with default iteration (" . number_format($iterations, 0, '.', ' ') . ") and default average rounds (" . number_format($averageOver, 0, '.', ' ') . ") 22 | options : 23 | -i=[0-9] Number of iterations. Each test will run this many time 24 | -a=[0-9] Compute an average over this many test. Each test will execute 25 | execute all its iterations this many time. 26 | "); 27 | exit; 28 | } 29 | 30 | for ($i = 1; $i <=2; $i++) { 31 | if (!empty($argv[$i])) { 32 | if (preg_match('`^-i=([0-9]+)$`', $argv[$i], $match)) { 33 | $iterations = max(1, (int) $match[1]); 34 | } else if (preg_match('`^-a=([0-9]+)$`', $argv[$i], $match)) { 35 | $averageOver = max(1, (int) $match[1]); 36 | } 37 | } 38 | } 39 | 40 | define('ITERATIONS', $iterations); 41 | define('AVG_OVER', $averageOver); 42 | $param = null; 43 | $benchList = [ 44 | 'instance' => [ 45 | 'invocationStr' => '[$instance, \'method\']', 46 | 'callable' => [new InstanceTest, 'methodTest'], 47 | 'vs' => [ 48 | 'call_user_func' => 1, 49 | 'call_user_func_array' => 1, 50 | 'Invoke' => 1, 51 | 'assignedClosureFactory' => 1, 52 | 'ClosureFactory' => 1, 53 | 'InvokeCallUserFunc' => 1, 54 | 'directImplementation' => 1, 55 | 'directInstance' => 1, 56 | ], 57 | ], 58 | 'static' => [ 59 | 'invocationStr' => '\'Class::method\'', 60 | 'callable' => 'StaticTest::methodTest', 61 | 'vs' => [ 62 | 'call_user_func' => 1, 63 | 'call_user_func_array' => 1, 64 | 'Invoke' => 1, 65 | 'assignedClosureFactory' => 1, 66 | 'ClosureFactory' => 1, 67 | 'InvokeCallUserFunc' => 1, 68 | 'directImplementation' => 1, 69 | 'directStatic' => 1, 70 | ], 71 | ], 72 | 73 | 'function' => [ 74 | 'invocationStr' => '\'function\'', 75 | 'callable' => 'functionTest', 76 | 'vs' => [ 77 | 'call_user_func' => 1, 78 | 'call_user_func_array' => 1, 79 | 'Invoke' => 1, 80 | 'assignedClosureFactory' => 1, 81 | 'ClosureFactory' => 1, 82 | 'InvokeCallUserFunc' => 1, 83 | 'directImplementation' => 1, 84 | 'directFunction' => 1, 85 | ], 86 | ], 87 | 'lambda' => [ 88 | 'invocationStr' => 'function($param) { return $param }', 89 | 'callable' => $lambdaTest, 90 | 'vs' => [ 91 | 'call_user_func' => 1, 92 | 'call_user_func_array' => 1, 93 | 'Invoke' => 1, 94 | 'assignedClosureFactory' => 1, 95 | 'ClosureFactory' => 1, 96 | 'InvokeCallUserFunc' => 1, 97 | 'directImplementation' => 1, 98 | 'directLambda' => 1, 99 | ], 100 | ], 101 | 'closure' => [ 102 | 'invocationStr' => 'function($param) use($use) { return $param }', 103 | 'callable' => $closureTest, 104 | 'vs' => [ 105 | 'call_user_func' => 1, 106 | 'call_user_func_array' => 1, 107 | 'Invoke' => 1, 108 | 'assignedClosureFactory' => 1, 109 | 'ClosureFactory' => 1, 110 | 'InvokeCallUserFunc' => 1, 111 | 'directImplementation' => 1, 112 | 'directClosure' => 1, 113 | ], 114 | ], 115 | ]; 116 | 117 | $report = []; 118 | exec('php -v', $phpVersion); 119 | $output->writeln("Benchmarking call_user_func"); 120 | $output->writeln('Iterations: ' . number_format($iterations, 0, '.', ' ') . ""); 121 | $output->writeln('Averaged over: ' . number_format($averageOver, 0, '.', ' ') . ""); 122 | $output->writeln('' . implode(PHP_EOL, $phpVersion) . ''); 123 | $output->writeln('' . php_uname() . ''); 124 | 125 | // static calls 126 | $directTimes = [ 127 | 'directStatic' => 0, 128 | 'directInstance' => 0, 129 | 'directFunction' => 0, 130 | 'directLambda' => 0, 131 | 'directClosure' => 0, 132 | ]; 133 | 134 | foreach ($directTimes as $key => $value) { 135 | $function = 'bench' . ucfirst($key); 136 | $directTimes[$key] = $function($param); 137 | } 138 | 139 | $averages = []; 140 | 141 | // dynamic calls 142 | foreach ($benchList as $key => $value) { 143 | $callable = $value['callable']; 144 | $report[$key] = []; 145 | 146 | foreach ($value['vs'] as $vs => $ignore) { 147 | $benchFunc = 'bench' . ucfirst($vs); 148 | $report[$key][$vs]['time'] = $benchFunc($callable, $param); 149 | //if (!isset($directTimes[$vs])) { 150 | $averages[$vs][] = $report[$key][$vs]['time']; 151 | //} 152 | 153 | if ($vs === 'call_user_function') { 154 | $report[$key]['diff'] = 0; 155 | $report[$key]['pct'] = 0; 156 | } else { 157 | calcVs($report[$key], $vs); 158 | } 159 | } 160 | 161 | // compute vs direct* 162 | foreach ($directTimes as $vs => $time) { 163 | if (isset($value[$vs])) { 164 | $report[$key][$vs]['time'] = $time; 165 | calcVs($report[$key], $vs); 166 | } 167 | } 168 | 169 | uksort($report[$key], "sortReportByDiff"); 170 | } 171 | 172 | foreach ($averages as $k => $v) { 173 | $averages[$k] = array_sum($averages[$k]) / count($averages[$k]); 174 | } 175 | 176 | natsort($averages); 177 | 178 | $localHeaders = ['method' => 'Invocation', 'time' => 'Time (s)','diff' => 'Delta (s)', 'pct' => '%']; 179 | $localRowDefaults = ['method' => '', 'time' => '','diff' => '', 'pct' => '']; 180 | 181 | foreach ($report as $type => $bench) { 182 | $output->writeln(''); 183 | $output->writeln("$type ~ {$benchList[$type]['invocationStr']}"); 184 | 185 | $localTable = new Table($output); 186 | foreach ($bench as $key => $value) { 187 | $localRows = $localRowDefaults; 188 | if ($key === 'call_user_func') { 189 | $localRows['method'] = "$key"; 190 | $localRows['diff'] = $localRows['pct'] = ''; 191 | } else { 192 | if ($key === 'call_user_func_array') { 193 | $localRows['method'] = "$key"; 194 | } else { 195 | $localRows['method'] = $key; 196 | } 197 | $localRows['diff'] = formatDiff(number_format($value['diff'], 4, '.', ' '), $value['diff']); 198 | $localRows['pct'] = formatDiff(number_format($value['pct'], 2, '.', ' '), $value['pct']); 199 | } 200 | 201 | $localRows['time'] = formatDiff(number_format($value['time'], 4, '.', ' '), $value['diff'], ''); 202 | 203 | $localTable->addRow($localRows); 204 | } 205 | $localTable->setHeaders($localHeaders)->render(); 206 | } 207 | 208 | $localTable = new Table($output); 209 | $output->writeln(''); 210 | $output->writeln("Overall Average"); 211 | $call_user_func_avg = $averages['call_user_func']; 212 | foreach ($averages as $vs => $time) { 213 | $localRows = $localRowDefaults; 214 | if ($vs === 'call_user_func') { 215 | $localRows['method'] = "$vs"; 216 | $diff = $localRows['diff'] = $localRows['pct'] = ''; 217 | } else { 218 | if ($vs === 'call_user_func_array') { 219 | $localRows['method'] = "$vs"; 220 | } else { 221 | $localRows['method'] = $vs; 222 | } 223 | $diff = $time - $call_user_func_avg; 224 | $pct = $diff * 100 / $call_user_func_avg; 225 | $localRows['diff'] = formatDiff(number_format($diff, 4, '.', ' '), $diff); 226 | $localRows['pct'] = formatDiff(number_format($pct, 2, '.', ' '), $pct); 227 | } 228 | 229 | $localRows['time'] = formatDiff(number_format($time, 4, '.', ' '), $diff, ''); 230 | $localTable->addRow($localRows); 231 | } 232 | $localTable->setHeaders($localHeaders)->render(); 233 | 234 | $output->writeln(''); 235 | $output->writeln(PHP_Timer::resourceUsage()); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # call_user_func 2 | 3 | [call_user_func()](https://php.net/call_user_func) and [call_user_func_array()](https://php.net/call_user_func_array) are often mentioned as "slow". At some point I needed to know by how much this could impact processes involving very large amount of Callable calls. 4 | 5 | ## The problem 6 | 7 | A Callable can be of many forms, either a string, an array a lambda or a Closure. 8 | If you want to generically call a Callable, the easy path is to just call [call_user_func()](https://php.net/call_user_func) : 9 | ```php 10 | $result = call_user_func($callable); 11 | ``` 12 | 13 | or [call_user_func_array()](https://php.net/call_user_func_array) : 14 | ```php 15 | $result = call_user_func_array($callable); 16 | ``` 17 | 18 | This has the great advantage of simplicity, but unfortunately, it is slower than direct invocation. 19 | This one liner also hides some complexity as each Callable type will need it's own invocation method. If we where to do it manually, we would use something like : 20 | 21 | ```php 22 | if (is_string($callable)) { 23 | $callable = trim($callable, '\\'); 24 | if (strpos($callable, '::')) { 25 | list($class, $method) = explode('::', $callable); 26 | $class::$method(); 27 | } else { 28 | $callable(); 29 | } 30 | } else if (is_array($callable)) { 31 | $instance = current($callable); 32 | $method = next($callable); 33 | $instance->$method(); 34 | } else { 35 | $callable(); 36 | } 37 | ``` 38 | 39 | Someone with more attention to estheticism could end up with a "smarter" solution using a simple "Closure Factory" : 40 | 41 | ```php 42 | /** 43 | * 44 | * @param Callable $callable 45 | * @return \Closure 46 | */ 47 | function closureFactory(Callable $callable) 48 | { 49 | if (is_string($callable)) { 50 | $callable = trim($callable, '\\'); 51 | if (strpos($callable, '::')) { 52 | list($class, $method) = explode('::', $callable); 53 | return function () use ($class, $method) { 54 | return $class::$method(); 55 | }; 56 | } else { 57 | return function () use ($callable) { 58 | return $callable(); 59 | }; 60 | } 61 | } else if (is_array($callable)) { 62 | return function () use ($callable) { 63 | return $callable[0]->$callable[1](); 64 | }; 65 | } else { 66 | return function () use ($callable) { 67 | return $callable(); 68 | }; 69 | } 70 | } 71 | ``` 72 | 73 | wich would later allow things like : 74 | ```php 75 | $closure = closureFactory($callable); 76 | $result = $closure(); 77 | ``` 78 | 79 | In addition to being better organized, it allows reuse of the call for no additional cost, except reassigning in object context without php7 since calling : 80 | 81 | ```php 82 | $instance->closure = closureFactory($callable); 83 | $result = $instance->closure(); 84 | ``` 85 | 86 | will not work and : 87 | ```php 88 | $instance->closure = closureFactory($callable); 89 | $result = ($instance->closure)(); 90 | ``` 91 | 92 | will only work with php7. Bellow that you're stuck with : 93 | ```php 94 | $instance->closure = closureFactory($callable); 95 | $closure = $instance->closure; 96 | $result = $closure(); 97 | ``` 98 | 99 | wich brings a bit of overhead and complexity. 100 | 101 | ## Finding out 102 | 103 | Exploring the options, I came up with a silly class, "Invoke", which wraps the Callable into a specialized class carrying the "fastest" invocation method. It seems insane at first because doing this involve wrapping the call into a class method, and in several cases, manipulating variables upon each call. 104 | 105 | Invoke comes with a factory providing with the "best" instance for each Callable type : 106 | ```php 107 | $Invoker = InvokeFactory::create($callable); 108 | // ... 109 | $Invoker->exec($param); 110 | ``` 111 | 112 | ### Benchmarking 113 | 114 | Of course, benchmarking does not tell everything and some benchmarks may even fail to prove the truth. In this case, I just timed the time taken to execute a number of calls of each recipe, and averaged over another number. Default for each test is 100 000 iterations averaged over 10 consecutive run (of 100k iteration each). 115 | It's not perfect by nature, as many thing happens in a modern computer, but you can use bigger number to tend to better results. 116 | 117 | If you wish to try, clone the repo and run 118 | ```bash 119 | $ composer install --dev 120 | ``` 121 | 122 | Then the benchmark can be run using the ./bench script : 123 | ```bash 124 | $ php bench 125 | ``` 126 | 127 | You can additionally set the number of iteration and average rounds : 128 | ```bash 129 | $ php bench --help 130 | bench usage 131 | no options : run with default iteration (100 000) and default average rounds (10) 132 | options : 133 | -i=[0-9] Number of iterations. Each test will run this many time 134 | -a=[0-9] Compute an average over this many test. Each test will execute 135 | execute all its iterations this many time. 136 | ``` 137 | 138 | The idea is to compare each case with various ways of calling the Callable. Since the primary goal was to compare invocation times, the dummy function/method/static/lambda/closure are all following the same synopsis : 139 | ```php 140 | function ($param) { 141 | return $param; 142 | } 143 | ``` 144 | 145 | As the first results started to show, I added an even sillier test case which does the same thing as Invoke except it ends up calling call_user_func() instead of trying to be efficient. The idea behind it is to get an estimate of Invoke own overhead, since : 146 | > invoke_time ~= invoke_overhead + recipe_exec_time 147 | 148 | which when : 149 | > recipe_exec_time ~= call_user_func_time 150 | 151 | tells us 152 | > invoke_overhead ~= invoke_time - call_user_func_time 153 | 154 | Again, it's not math, it's benchmarking ^^ 155 | 156 | I ran test against both closure factory and assigned closure factory to measure the cost of closure assignment at run-time. 157 | 158 | ### Callable tested 159 | With `$param` explicitly set to null before benchmark starts 160 | * Instance 161 | 162 | ```php 163 | $instance = [$instance, 'method']; 164 | ``` 165 | * Static 166 | 167 | ```php 168 | $static = 'ClassName::method'; 169 | ``` 170 | * Function 171 | 172 | ```php 173 | $function = 'functionName'; 174 | ``` 175 | * Lambda 176 | 177 | ```php 178 | $lambda = function($param) { 179 | return $param; 180 | }; 181 | ``` 182 | * Closure 183 | 184 | With `$use` explicitly set to null before benchmark starts 185 | ```php 186 | $closure = function($param) use ($use) { 187 | return $param; 188 | }; 189 | ``` 190 | 191 | ### Invocation tested 192 | * call_user_func 193 | 194 | ```php 195 | // in test loop 196 | call_user_func($callable, $param); 197 | ``` 198 | * call_user_func_array 199 | 200 | ```php 201 | // in test loop 202 | call_user_func_array($callable, $arrayParam); 203 | ``` 204 | * directFunction 205 | 206 | ```php 207 | // in test loop 208 | fucntionName($param); 209 | ``` 210 | * directStatic 211 | 212 | ```php 213 | // in test loop 214 | ClassName::method($param); 215 | ``` 216 | * directInstance 217 | 218 | ```php 219 | // in test loop 220 | $instance->method($param); 221 | ``` 222 | * directLambda 223 | 224 | ```php 225 | // in test loop 226 | $lambda($param); 227 | ``` 228 | * directClosure 229 | 230 | ```php 231 | // in test loop 232 | $closure($param); 233 | ``` 234 | * ClosureFactory 235 | 236 | ```php 237 | // before test loop 238 | $closure = ClosureFactory::create($callable); 239 | // in test loop 240 | $closure($param); 241 | ``` 242 | * assignedClosureFactory 243 | 244 | ```php 245 | // before test loop 246 | $closure = ClosureFactory::create($callable); 247 | // in test loop 248 | $call = $closure; 249 | $call($param); 250 | ``` 251 | * directImplementation 252 | 253 | ```php 254 | // in test loop 255 | if (is_string($callable)) { 256 | $callable = trim($callable, '\\'); 257 | if (strpos($callable, '::')) { 258 | list($class, $method) = explode('::', $callable); 259 | $class::$method($param); 260 | } else { 261 | $callable($param); 262 | } 263 | } else if (is_array($callable)) { 264 | $class = $callable[0]; 265 | $method = $callable[1]; 266 | $class->{$method}($param); 267 | } else { 268 | $callable($param); 269 | } 270 | ``` 271 | * Invoke 272 | 273 | ```php 274 | // before test loop 275 | $instance = InvokeFactory::create($callable); 276 | // in test loop 277 | $instance->execOneArg($param); 278 | ``` 279 | * InvokeCallUserFunc 280 | 281 | ```php 282 | // before test loop 283 | $instance = new InvokeCallUserFunc($callable); 284 | // in test loop 285 | $instance->execOneArg($param); 286 | ``` 287 | 288 | ## Conclusion 289 | 290 | First thing to note is that call_user_func() has been improved a lot with php7. It's about 3x faster average with 7.1.2 compared with 5.6.30. 291 | With 5.6.30, call_user_func almost always looses against Invoke, which in itself is interesting, especially if we evaluate Invoke's own overhead comparing with InvokeCallUserFunc case. 292 | call_user_func_array() is always slower than call_user_func(), which is not a surprise, but again, it is much slower with 5.6.30. 293 | 294 | Of course, if you think about real world scenario, if 60% slower is significant, looking at timings show we're talking about fractions of a second every million call, with each million call costing around half a second. 295 | I can't think of many use cases where one would need to call millions of functions to build a web page, and few background process would actually loose so much with this 0.3 second lost every million call. 296 | 297 | So as a practical conclusion, using call_user_func() is perfectly sane, even with php 5.6.30 (I did not tested bellow that). 298 | 299 | Analyzing further, some interesting things to note are : 300 | * The few ifs of a direct implementation are costing too much 301 | * closure factory is surprisingly slow 302 | * assigned closure factory is as expected a bit slower than simple closure factory, but it's not very significant 303 | * php7 is a lot faster, with a bit more ram usage, but this should not actually matter since php7 has way better ram handling / sharing overall 304 | * Not much difference between php 7.1 and 7.2 305 | 306 | ## Results 307 | 308 | Test ran on win10 with Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz 309 | 310 | ### PHP 5.6.30 311 | 312 | ```shell 313 | $ php bench 314 | Benchmarking call_user_func 315 | Iterations: 100 000 316 | Averaged over: 10 317 | PHP 5.6.30 (cli) (built: Jan 18 2017 19:47:28) 318 | Copyright (c) 1997-2016 The PHP Group 319 | Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies 320 | Windows NT 10.0 build 14393 (Windows 10) AMD64 321 | 322 | instance ~ [$instance, 'method'] 323 | +------------------------+----------+-----------+--------+ 324 | | Invocation | Time (s) | Delta (s) | % | 325 | +------------------------+----------+-----------+--------+ 326 | | directInstance | 0.0116 | -0.0168 | -59.13 | 327 | | call_user_func | 0.0284 | | | 328 | | Invoke | 0.0301 | +0.0017 | +6.11 | 329 | | ClosureFactory | 0.0370 | +0.0086 | +30.13 | 330 | | directImplementation | 0.0371 | +0.0087 | +30.48 | 331 | | assignedClosureFactory | 0.0377 | +0.0093 | +32.83 | 332 | | call_user_func_array | 0.0393 | +0.0109 | +38.29 | 333 | | InvokeCallUserFunc | 0.0395 | +0.0111 | +39.26 | 334 | +------------------------+----------+-----------+--------+ 335 | 336 | static ~ 'Class::method' 337 | +------------------------+----------+-----------+---------+ 338 | | Invocation | Time (s) | Delta (s) | % | 339 | +------------------------+----------+-----------+---------+ 340 | | directStatic | 0.0098 | -0.0270 | -73.38 | 341 | | Invoke | 0.0331 | -0.0037 | -10.03 | 342 | | call_user_func | 0.0367 | | | 343 | | ClosureFactory | 0.0396 | +0.0029 | +7.84 | 344 | | assignedClosureFactory | 0.0400 | +0.0033 | +8.99 | 345 | | call_user_func_array | 0.0463 | +0.0095 | +25.97 | 346 | | InvokeCallUserFunc | 0.0496 | +0.0129 | +35.02 | 347 | | directImplementation | 0.0942 | +0.0575 | +156.46 | 348 | +------------------------+----------+-----------+---------+ 349 | 350 | function ~ 'function' 351 | +------------------------+----------+-----------+--------+ 352 | | Invocation | Time (s) | Delta (s) | % | 353 | +------------------------+----------+-----------+--------+ 354 | | directFunction | 0.0089 | -0.0230 | -72.03 | 355 | | Invoke | 0.0261 | -0.0058 | -18.25 | 356 | | ClosureFactory | 0.0281 | -0.0039 | -12.15 | 357 | | assignedClosureFactory | 0.0296 | -0.0024 | -7.53 | 358 | | call_user_func | 0.0320 | | | 359 | | call_user_func_array | 0.0416 | +0.0097 | +30.23 | 360 | | InvokeCallUserFunc | 0.0442 | +0.0122 | +38.31 | 361 | | directImplementation | 0.0521 | +0.0201 | +62.96 | 362 | +------------------------+----------+-----------+--------+ 363 | 364 | lambda ~ function($param) { return $param } 365 | +------------------------+----------+-----------+--------+ 366 | | Invocation | Time (s) | Delta (s) | % | 367 | +------------------------+----------+-----------+--------+ 368 | | directLambda | 0.0109 | -0.0135 | -55.15 | 369 | | Invoke | 0.0226 | -0.0018 | -7.30 | 370 | | ClosureFactory | 0.0243 | -0.0001 | -0.40 | 371 | | call_user_func | 0.0244 | | | 372 | | directImplementation | 0.0251 | +0.0007 | +2.91 | 373 | | assignedClosureFactory | 0.0263 | +0.0019 | +7.59 | 374 | | call_user_func_array | 0.0342 | +0.0098 | +40.09 | 375 | | InvokeCallUserFunc | 0.0356 | +0.0112 | +45.93 | 376 | +------------------------+----------+-----------+--------+ 377 | 378 | closure ~ function($param) use($use) { return $param } 379 | +------------------------+----------+-----------+--------+ 380 | | Invocation | Time (s) | Delta (s) | % | 381 | +------------------------+----------+-----------+--------+ 382 | | directClosure | 0.0150 | -0.0135 | -47.51 | 383 | | call_user_func | 0.0285 | | | 384 | | ClosureFactory | 0.0288 | +0.0003 | +1.20 | 385 | | Invoke | 0.0289 | +0.0004 | +1.31 | 386 | | directImplementation | 0.0289 | +0.0004 | +1.50 | 387 | | assignedClosureFactory | 0.0303 | +0.0019 | +6.50 | 388 | | call_user_func_array | 0.0381 | +0.0097 | +33.91 | 389 | | InvokeCallUserFunc | 0.0398 | +0.0113 | +39.60 | 390 | +------------------------+----------+-----------+--------+ 391 | 392 | Overall Average 393 | +------------------------+----------+-----------+--------+ 394 | | Invocation | Time (s) | Delta (s) | % | 395 | +------------------------+----------+-----------+--------+ 396 | | directFunction | 0.0089 | -0.0211 | -70.19 | 397 | | directStatic | 0.0098 | -0.0202 | -67.39 | 398 | | directLambda | 0.0109 | -0.0191 | -63.52 | 399 | | directInstance | 0.0116 | -0.0184 | -61.31 | 400 | | directClosure | 0.0150 | -0.0150 | -50.15 | 401 | | Invoke | 0.0282 | -0.0018 | -6.13 | 402 | | call_user_func | 0.0300 | | | 403 | | ClosureFactory | 0.0316 | +0.0016 | +5.20 | 404 | | assignedClosureFactory | 0.0328 | +0.0028 | +9.28 | 405 | | call_user_func_array | 0.0399 | +0.0099 | +33.02 | 406 | | InvokeCallUserFunc | 0.0418 | +0.0118 | +39.17 | 407 | | directImplementation | 0.0475 | +0.0175 | +58.28 | 408 | +------------------------+----------+-----------+--------+ 409 | 410 | Time: 13.83 seconds, Memory: 1.00MB 411 | ``` 412 | 413 | ### PHP 7.1.2 414 | 415 | ```shell 416 | $ php bench 417 | Benchmarking call_user_func 418 | Iterations: 100 000 419 | Averaged over: 10 420 | PHP 7.1.2 (cli) (built: Feb 14 2017 21:24:45) ( NTS MSVC14 421 | Copyright (c) 1997-2017 The PHP Group 422 | Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologi 423 | Windows NT 10.0 build 14393 (Windows 10) AMD64 424 | 425 | instance ~ [$instance, 'method'] 426 | +------------------------+----------+-----------+--------+ 427 | | Invocation | Time (s) | Delta (s) | % | 428 | +------------------------+----------+-----------+--------+ 429 | | directInstance | 0.0058 | -0.0080 | -58.11 | 430 | | call_user_func | 0.0138 | | | 431 | | call_user_func_array | 0.0148 | +0.0009 | +6.74 | 432 | | directImplementation | 0.0158 | +0.0019 | +13.93 | 433 | | Invoke | 0.0188 | +0.0050 | +36.06 | 434 | | ClosureFactory | 0.0215 | +0.0076 | +55.29 | 435 | | assignedClosureFactory | 0.0222 | +0.0084 | +60.48 | 436 | | InvokeCallUserFunc | 0.0254 | +0.0115 | +83.44 | 437 | +------------------------+----------+-----------+--------+ 438 | 439 | static ~ 'Class::method' 440 | +------------------------+----------+-----------+--------+ 441 | | Invocation | Time (s) | Delta (s) | % | 442 | +------------------------+----------+-----------+--------+ 443 | | directStatic | 0.0050 | -0.0236 | -82.55 | 444 | | Invoke | 0.0273 | -0.0013 | -4.54 | 445 | | ClosureFactory | 0.0285 | -0.0001 | -0.43 | 446 | | call_user_func | 0.0286 | | | 447 | | call_user_func_array | 0.0295 | +0.0009 | +3.03 | 448 | | assignedClosureFactory | 0.0296 | +0.0010 | +3.59 | 449 | | InvokeCallUserFunc | 0.0424 | +0.0138 | +48.27 | 450 | | directImplementation | 0.0534 | +0.0248 | +86.82 | 451 | +------------------------+----------+-----------+--------+ 452 | 453 | function ~ 'function' 454 | +------------------------+----------+-----------+---------+ 455 | | Invocation | Time (s) | Delta (s) | % | 456 | +------------------------+----------+-----------+---------+ 457 | | directFunction | 0.0043 | -0.0072 | -62.59 | 458 | | call_user_func | 0.0115 | | | 459 | | call_user_func_array | 0.0123 | +0.0007 | +6.42 | 460 | | Invoke | 0.0188 | +0.0073 | +63.31 | 461 | | ClosureFactory | 0.0194 | +0.0078 | +68.02 | 462 | | assignedClosureFactory | 0.0206 | +0.0091 | +79.05 | 463 | | InvokeCallUserFunc | 0.0235 | +0.0120 | +104.16 | 464 | | directImplementation | 0.0268 | +0.0153 | +132.95 | 465 | +------------------------+----------+-----------+---------+ 466 | 467 | lambda ~ function($param) { return $param } 468 | +------------------------+----------+-----------+---------+ 469 | | Invocation | Time (s) | Delta (s) | % | 470 | +------------------------+----------+-----------+---------+ 471 | | directLambda | 0.0063 | -0.0004 | -6.35 | 472 | | call_user_func | 0.0067 | | | 473 | | call_user_func_array | 0.0076 | +0.0008 | +12.17 | 474 | | directImplementation | 0.0091 | +0.0023 | +34.19 | 475 | | Invoke | 0.0133 | +0.0066 | +97.79 | 476 | | ClosureFactory | 0.0156 | +0.0088 | +131.14 | 477 | | assignedClosureFactory | 0.0172 | +0.0105 | +155.52 | 478 | | InvokeCallUserFunc | 0.0187 | +0.0120 | +177.78 | 479 | +------------------------+----------+-----------+---------+ 480 | 481 | closure ~ function($param) use($use) { return $param } 482 | +------------------------+----------+-----------+---------+ 483 | | Invocation | Time (s) | Delta (s) | % | 484 | +------------------------+----------+-----------+---------+ 485 | | directClosure | 0.0081 | -0.0005 | -6.31 | 486 | | call_user_func | 0.0086 | | | 487 | | call_user_func_array | 0.0093 | +0.0007 | +8.02 | 488 | | directImplementation | 0.0111 | +0.0024 | +28.31 | 489 | | Invoke | 0.0151 | +0.0064 | +74.18 | 490 | | ClosureFactory | 0.0187 | +0.0101 | +116.39 | 491 | | assignedClosureFactory | 0.0197 | +0.0110 | +127.78 | 492 | | InvokeCallUserFunc | 0.0222 | +0.0135 | +156.45 | 493 | +------------------------+----------+-----------+---------+ 494 | 495 | Overall Average 496 | +------------------------+----------+-----------+--------+ 497 | | Invocation | Time (s) | Delta (s) | % | 498 | +------------------------+----------+-----------+--------+ 499 | | directFunction | 0.0043 | -0.0096 | -68.92 | 500 | | directStatic | 0.0050 | -0.0089 | -64.04 | 501 | | directInstance | 0.0058 | -0.0081 | -58.22 | 502 | | directLambda | 0.0063 | -0.0075 | -54.44 | 503 | | directClosure | 0.0081 | -0.0058 | -41.57 | 504 | | call_user_func | 0.0139 | | | 505 | | call_user_func_array | 0.0147 | +0.0008 | +5.84 | 506 | | Invoke | 0.0187 | +0.0048 | +34.61 | 507 | | ClosureFactory | 0.0207 | +0.0069 | +49.43 | 508 | | assignedClosureFactory | 0.0219 | +0.0080 | +57.75 | 509 | | directImplementation | 0.0232 | +0.0094 | +67.53 | 510 | | InvokeCallUserFunc | 0.0264 | +0.0126 | +90.67 | 511 | +------------------------+----------+-----------+--------+ 512 | 513 | Time: 7.69 seconds, Memory: 2.00MB 514 | ``` 515 | 516 | ### PHP 7.2.0 517 | 518 | ```shell 519 | $ php bench 520 | Benchmarking call_user_func 521 | Iterations: 100 000 522 | Averaged over: 10 523 | PHP 7.2.0 (cli) (built: Nov 28 2017 23:48:32) ( NTS MSVC15 (Visual C++ 2017) x64 ) 524 | Copyright (c) 1997-2017 The PHP Group 525 | Zend Engine v3.2.0, Copyright (c) 1998-2017 Zend Technologies 526 | Windows NT 10.0 build 17134 (Windows 10) AMD64 527 | 528 | instance ~ [$instance, 'method'] 529 | +------------------------+----------+-----------+--------+ 530 | | Invocation | Time (s) | Delta (s) | % | 531 | +------------------------+----------+-----------+--------+ 532 | | directInstance | 0.0060 | -0.0070 | -53.88 | 533 | | call_user_func | 0.0131 | | | 534 | | call_user_func_array | 0.0136 | +0.0006 | +4.29 | 535 | | directImplementation | 0.0163 | +0.0032 | +24.67 | 536 | | Invoke | 0.0193 | +0.0063 | +47.97 | 537 | | ClosureFactory | 0.0218 | +0.0087 | +66.62 | 538 | | assignedClosureFactory | 0.0230 | +0.0100 | +76.26 | 539 | | InvokeCallUserFunc | 0.0248 | +0.0117 | +89.60 | 540 | +------------------------+----------+-----------+--------+ 541 | 542 | static ~ 'Class::method' 543 | +------------------------+----------+-----------+--------+ 544 | | Invocation | Time (s) | Delta (s) | % | 545 | +------------------------+----------+-----------+--------+ 546 | | directStatic | 0.0053 | -0.0234 | -81.62 | 547 | | Invoke | 0.0241 | -0.0046 | -15.94 | 548 | | call_user_func_array | 0.0284 | -0.0004 | -1.29 | 549 | | ClosureFactory | 0.0286 | -0.0001 | -0.30 | 550 | | call_user_func | 0.0287 | | | 551 | | assignedClosureFactory | 0.0298 | +0.0011 | +3.82 | 552 | | InvokeCallUserFunc | 0.0392 | +0.0105 | +36.61 | 553 | | directImplementation | 0.0527 | +0.0239 | +83.33 | 554 | +------------------------+----------+-----------+--------+ 555 | 556 | function ~ 'function' 557 | +------------------------+----------+-----------+---------+ 558 | | Invocation | Time (s) | Delta (s) | % | 559 | +------------------------+----------+-----------+---------+ 560 | | directFunction | 0.0046 | -0.0068 | -59.38 | 561 | | call_user_func | 0.0114 | | | 562 | | call_user_func_array | 0.0120 | +0.0006 | +4.87 | 563 | | Invoke | 0.0194 | +0.0080 | +69.58 | 564 | | ClosureFactory | 0.0203 | +0.0089 | +77.65 | 565 | | InvokeCallUserFunc | 0.0231 | +0.0117 | +101.96 | 566 | | assignedClosureFactory | 0.0236 | +0.0122 | +106.44 | 567 | | directImplementation | 0.0246 | +0.0132 | +115.53 | 568 | +------------------------+----------+-----------+---------+ 569 | 570 | lambda ~ function($param) { return $param } 571 | +------------------------+----------+-----------+---------+ 572 | | Invocation | Time (s) | Delta (s) | % | 573 | +------------------------+----------+-----------+---------+ 574 | | call_user_func | 0.0068 | | | 575 | | directLambda | 0.0071 | +0.0003 | +4.25 | 576 | | call_user_func_array | 0.0071 | +0.0003 | +4.90 | 577 | | directImplementation | 0.0094 | +0.0026 | +38.15 | 578 | | ClosureFactory | 0.0143 | +0.0075 | +110.75 | 579 | | Invoke | 0.0145 | +0.0077 | +113.12 | 580 | | assignedClosureFactory | 0.0154 | +0.0086 | +126.33 | 581 | | InvokeCallUserFunc | 0.0179 | +0.0111 | +163.06 | 582 | +------------------------+----------+-----------+---------+ 583 | 584 | closure ~ function($param) use($use) { return $param } 585 | +------------------------+----------+-----------+---------+ 586 | | Invocation | Time (s) | Delta (s) | % | 587 | +------------------------+----------+-----------+---------+ 588 | | directClosure | 0.0085 | -0.0004 | -4.16 | 589 | | call_user_func | 0.0089 | | | 590 | | call_user_func_array | 0.0089 | +0.0000 | +0.16 | 591 | | directImplementation | 0.0111 | +0.0022 | +24.76 | 592 | | Invoke | 0.0154 | +0.0065 | +72.94 | 593 | | ClosureFactory | 0.0179 | +0.0090 | +100.94 | 594 | | assignedClosureFactory | 0.0186 | +0.0097 | +109.08 | 595 | | InvokeCallUserFunc | 0.0203 | +0.0114 | +128.14 | 596 | +------------------------+----------+-----------+---------+ 597 | 598 | Overall Average 599 | +------------------------+----------+-----------+--------+ 600 | | Invocation | Time (s) | Delta (s) | % | 601 | +------------------------+----------+-----------+--------+ 602 | | directFunction | 0.0046 | -0.0091 | -66.30 | 603 | | directStatic | 0.0053 | -0.0085 | -61.71 | 604 | | directInstance | 0.0060 | -0.0078 | -56.29 | 605 | | directLambda | 0.0071 | -0.0067 | -48.52 | 606 | | directClosure | 0.0085 | -0.0053 | -38.15 | 607 | | call_user_func | 0.0138 | | | 608 | | call_user_func_array | 0.0140 | +0.0002 | +1.59 | 609 | | Invoke | 0.0186 | +0.0048 | +34.58 | 610 | | ClosureFactory | 0.0206 | +0.0068 | +49.35 | 611 | | assignedClosureFactory | 0.0221 | +0.0083 | +60.26 | 612 | | directImplementation | 0.0228 | +0.0090 | +65.53 | 613 | | InvokeCallUserFunc | 0.0251 | +0.0113 | +81.80 | 614 | +------------------------+----------+-----------+--------+ 615 | 616 | Time: 7.97 seconds, Memory: 2.00MB 617 | ``` 618 | --------------------------------------------------------------------------------