今天早些时候,我正在处理一个 PHP 5.3+ 应用程序,这意味着我可以自由使用 PHP 闭包。我觉得太棒了!然后我遇到了一段代码,在这段代码中,使用函数式的 PHP 代码将使事情变得更加容易,但是,尽管我有一个逻辑上的答案,但它让我想知道在直接调用
正如我所想,后者确实更快,但差别并不是很大。事实上,在重复这些相同的测试10000次并取平均值时,差别只有0.05秒。可能是偶然事件。
这让我更加好奇。那么
然后,我不知道为什么要这样做,但我确实这样做了,我检查了
测试结果如下:
我运行了这两个测试各10000次,并查看结果并得出平均值。结果让我很惊讶。
结果表明,闭包函数这一次比较慢,大约慢了4倍。我觉得这不可能。我的意思是,通过
结果如下:
好奇为什么会这样,我检查了CPU使用情况和其他系统资源,并确保没有运行不必要的内容,一切都正常了,所以我再次运行测试,但结果相似。
于是我尝试了相同的测试,只运行了一次,并多次运行它(当然每次都计时)。结果发现闭包确实比
下面是我用来进行这些测试的代码。有人能告诉我到底发生了什么吗?是我的代码问题还是PHP出了问题?
array_map()
中的闭包和将其作为变量传递下去之间的性能影响。也就是以下两个示例:$test_array = array('test', 'test', 'test', 'test', 'test' );
array_map( function ( $item ) { return $item; }, $test_array );
并且
$test_array = array('test', 'test', 'test', 'test', 'test' );
$fn = function ( $item ) { return $item; };
array_map( $fn, $test_array );
正如我所想,后者确实更快,但差别并不是很大。事实上,在重复这些相同的测试10000次并取平均值时,差别只有0.05秒。可能是偶然事件。
这让我更加好奇。那么
create_function()
和闭包呢?根据经验,create_function()
在处理像array_map()
这样的函数时应该会更慢,因为它会创建一个函数,对其进行评估,然后将其存储下来。而正如我所想,create_function()
确实更慢。这都是使用array_map()
完成的。然后,我不知道为什么要这样做,但我确实这样做了,我检查了
create_function()
和闭包之间的差异,同时将其保存并只调用一次。没有处理,没有其他操作,只是简单地传递一个字符串,并返回该字符串。测试结果如下:
$fn = function($item) { return $item; };
$fn('test');
并且
$fn = create_function( '$item', 'return $item;' );
$fn('test');
我运行了这两个测试各10000次,并查看结果并得出平均值。结果让我很惊讶。
结果表明,闭包函数这一次比较慢,大约慢了4倍。我觉得这不可能。我的意思是,通过
array_map()
运行闭包函数要快得多,而通过变量运行相同的函数,然后通过array_map()
运行也更快,这几乎和这个测试相同。结果如下:
array
0 =>
array
'test' => string 'Closure test' (length=12)
'iterations' => int 10000
'time' => float 5.1327705383301E-6
1 =>
array
'test' => string 'Anonymous test' (length=14)
'iterations' => int 10000
'time' => float 1.6745710372925E-5
好奇为什么会这样,我检查了CPU使用情况和其他系统资源,并确保没有运行不必要的内容,一切都正常了,所以我再次运行测试,但结果相似。
于是我尝试了相同的测试,只运行了一次,并多次运行它(当然每次都计时)。结果发现闭包确实比
create_function()
慢4倍,除非偶尔有两到三倍速度比create_function()
更快,我猜这只是偶然事件,但似乎足以将测试1000次的时间缩短一半。下面是我用来进行这些测试的代码。有人能告诉我到底发生了什么吗?是我的代码问题还是PHP出了问题?
<?php
/**
* Simple class to benchmark code
*/
class Benchmark
{
/**
* This will contain the results of the benchmarks.
* There is no distinction between averages and just one runs
*/
private $_results = array();
/**
* Disable PHP's time limit and PHP's memory limit!
* These benchmarks may take some resources
*/
public function __construct() {
set_time_limit( 0 );
ini_set('memory_limit', '1024M');
}
/**
* The function that times a piece of code
* @param string $name Name of the test. Must not have been used before
* @param callable|closure $callback A callback for the code to run.
* @param boolean|integer $multiple optional How many times should the code be run,
* if false, only once, else run it $multiple times, and store the average as the benchmark
* @return Benchmark $this
*/
public function time( $name, $callback, $multiple = false )
{
if($multiple === false) {
// run and time the test
$start = microtime( true );
$callback();
$end = microtime( true );
// add the results to the results array
$this->_results[] = array(
'test' => $name,
'iterations' => 1,
'time' => $end - $start
);
} else {
// set a default if $multiple is set to true
if($multiple === true) {
$multiple = 10000;
}
// run the test $multiple times and time it every time
$total_time = 0;
for($i=1;$i<=$multiple;$i++) {
$start = microtime( true );
$callback();
$end = microtime( true );
$total_time += $end - $start;
}
// calculate the average and add it to the results
$this->_results[] = array(
'test' => $name,
'iterations' => $multiple,
'time' => $total_time/$multiple
);
}
return $this; //chainability
}
/**
* Returns all the results
* @return array $results
*/
public function get_results()
{
return $this->_results;
}
}
$benchmark = new Benchmark();
$benchmark->time( 'Closure test', function () {
$fn = function($item) { return $item; };
$fn('test');
}, true);
$benchmark->time( 'Anonymous test', function () {
$fn = create_function( '$item', 'return $item;' );
$fn('test');
}, true);
$benchmark->time( 'Closure direct', function () {
$test_array = array('test', 'test', 'test', 'test', 'test' );
$test_array = array_map( function ( $item ) { return $item; }, $test_array );
}, true);
$benchmark->time( 'Closure stored', function () {
$test_array = array('test', 'test', 'test', 'test', 'test' );
$fn = function ( $item ) { return $item; };
$test_array = array_map( $fn, $test_array );
}, true);
$benchmark->time( 'Anonymous direct', function () {
$test_array = array('test', 'test', 'test', 'test', 'test' );
$test_array = array_map( create_function( '$item', 'return $item;' ), $test_array );
}, true);
$benchmark->time( 'Anonymous stored', function () {
$test_array = array('test', 'test', 'test', 'test', 'test' );
$fn = create_function( '$item', 'return $item;' );
$test_array = array_map( $fn, $test_array );
}, true);
var_dump($benchmark->get_results());
这段代码的结果是:
array
0 =>
array
'test' => string 'Closure test' (length=12)
'iterations' => int 10000
'time' => float 5.4110765457153E-6
1 =>
array
'test' => string 'Anonymous test' (length=14)
'iterations' => int 10000
'time' => float 1.6784238815308E-5
2 =>
array
'test' => string 'Closure direct' (length=14)
'iterations' => int 10000
'time' => float 1.5178990364075E-5
3 =>
array
'test' => string 'Closure stored' (length=14)
'iterations' => int 10000
'time' => float 1.5463256835938E-5
4 =>
array
'test' => string 'Anonymous direct' (length=16)
'iterations' => int 10000
'time' => float 2.7537250518799E-5
5 =>
array
'test' => string 'Anonymous stored' (length=16)
'iterations' => int 10000
'time' => float 2.8293371200562E-5