4

今日の初め、私はPHP 5.3以降のアプリケーションに取り組んでいました。つまり、PHPクロージャを自由に使用できました。素晴らしいと思いました!次に、関数型PHPコードを使用すると非常に簡単になるコードに遭遇しましたが、論理的な答えを念頭に置いていたので、クロージャーを直接呼び出してarray_map()から渡すと、パフォーマンスにどのような影響があるのか​​疑問に思いました。変数としてダウンします。つまり、次の2つです。

$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()保存して1回呼び出すだけで、クロージャとの違いを確認しました。処理も何もありません。単に文字列を渡し、その文字列を返すだけです。

テストは次のようになりました。

$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使用率やその他のシステムリソースをチェックし、不要なものが実行されていないことを確認しました。すべてが正常であるため、テストを再実行しましたが、同様の結果が得られました。

そのため、同じテストを1回だけ試し、複数回実行しました(もちろん毎回タイミングを合わせます)。閉鎖は確かに4倍遅いことがわかりましたが、時々それはよりも約2〜3倍速くなるでしょう、それ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
4

2 に答える 2

19

5.1327705383301E-6は1.6745710372925E-5より4倍遅くはありません。約3倍高速です。あなたは数字を間違って読んでいます。すべての結果で、クロージャは一貫してより高速であるようcreate_functionです。

于 2012-07-01T21:14:15.097 に答える
2

このベンチマークを参照してください。

<?php
$iter = 100000;


$start = microtime(true);
for ($i = 0; $i < $iter; $i++) {}
$end = microtime(true) - $start;
echo "Loop overhead: ".PHP_EOL;
echo "$end seconds".PHP_EOL;

$start = microtime(true);
for ($i = 0; $i < $iter; $i++) {
    $fn = function($item) { return $item; };
    $fn('test');
}
$end = microtime(true) - $start;
echo "Lambda function: ".PHP_EOL;
echo "$end seconds".PHP_EOL;


$start = microtime(true);
for ($i = 0; $i < $iter; $i++) {
    $fn = create_function( '$item', 'return $item;' );
    $fn('test');
}
$end = microtime(true) - $start;
echo "Eval create function: ".PHP_EOL;
echo "$end seconds".PHP_EOL;

結果:

Loop overhead: 
0.011878967285156 seconds
Lambda function: 
0.067019939422607 seconds
Eval create function: 
1.5625419616699 seconds

ここで、興味深いことに、関数宣言をforループの外側に配置すると次のようになります。

Loop overhead: 
0.0057950019836426 seconds
Lambda function: 
0.030204057693481 seconds
Eval create function: 
0.040947198867798 seconds

そして、元の質問に答えるために、ラムダ関数を変数に割り当てることと単にそれを使用することの間に違いはありません。複数回使用しない限り、コードを明確にするために変数を使用する方が適切です。

于 2012-07-01T01:03:02.260 に答える