title: PHP closures give strange performance behaviour
body: <p>Earlier today I was working on a PHP 5.3+ application, which meant I was free to use PHP closures. Great I thought! Then I ran into a piece of code where the use of functional PHP code would make things a lot easier, but, while I had a logical answer in mind, it made me wonder what the performance impact between directly calling a closure within <code>array_map()</code> and passing it down as a variable. I.e. the following two:</p>
  
  <pre><code>$test_array = array('test', 'test', 'test', 'test', 'test' );
  array_map( function ( $item ) { return $item; }, $test_array );
  </code></pre>
  
  <p>and</p>
  
  <pre><code>$test_array = array('test', 'test', 'test', 'test', 'test' );
  $fn = function ( $item ) { return $item; };
  array_map( $fn, $test_array );
  </code></pre>
  
  <p>As I had thought, the latter was indeed faster, but the difference wasn't that big. In fact, the difference was 0.05 secs for repeating these same tests 10000 times and taking the average. May have even been a fluke.</p>
  
  <p>This made me even more curious. What about <code>create_function()</code> and closures? Again, experience tells me <code>create_function()</code> should be slower when it comes to something like <code>array_map()</code> as it creates a function, evaluates it, and then stores this as well.
  And again, as I had thought, <code>create_function()</code> was indeed slower. This was all with <code>array_map()</code>.</p>
  
  <p>Then, and I'm not sure why I did this, but I did, I checked what the difference was between <code>create_function()</code> and closures while saving it and just calling it once. No processing, no nothing, just simply passing a string, and returning that string.</p>
  
  <p>The tests became:</p>
  
  <pre><code>$fn = function($item) { return $item; };
  $fn('test');
  </code></pre>
  
  <p>and</p>
  
  <pre><code>$fn = create_function( '$item', 'return $item;' );
  $fn('test');
  </code></pre>
  
  <p>I ran both these tests 10000 times each, and looked at the results and got the average. I was quite surprised by the results.</p>
  
  <p>Turns out, the closure was about 4x slower this time. This couldn't be I thought. I mean, running a closure through <code>array_map()</code> was much faster, and running the same function through a variable through <code>array_map()</code> was even faster, which was practically the same as this test.</p>
  
  <p>The results were</p>
  
  <pre><code>array
    0 =&gt; 
      array
        'test' =&gt; string 'Closure test' (length=12)
        'iterations' =&gt; int 10000
        'time' =&gt; float 5.1327705383301E-6
    1 =&gt; 
      array
        'test' =&gt; string 'Anonymous test' (length=14)
        'iterations' =&gt; int 10000
        'time' =&gt; float 1.6745710372925E-5
  </code></pre>
  
  <p>So curious as to why it was doing this, I checked the CPU usage and other system resources and made sure nothing unnecessary was running, everything was fine now so I ran the tests again, but I got similar results.</p>
  
  <p>So I tried the same tests only once, and ran it multiple times (timing it every time of course). It turns out the closure was indeed 4 times slower, except every now and then it would be about two or three times faster than <code>create_function()</code>, which I'm guess were just flukes, but it seemed to be enough to cut the time by half for when I did the tests 1000 times.</p>
  
  <p>Below is the code I used to make these tests. Can anyone tell me what the hell is going on here? Is it my code or is it just PHP acting up?</p>
  
  <pre><code>&lt;?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-&gt;_results[] = array(
                  'test' =&gt; $name,
                  'iterations' =&gt; 1,
                  'time' =&gt; $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&lt;=$multiple;$i++) {
                  $start = microtime( true );
                  $callback();
                  $end = microtime( true );
                  $total_time += $end - $start;
              }
              // calculate the average and add it to the results
              $this-&gt;_results[] = array(
                  'test' =&gt; $name,
                  'iterations' =&gt; $multiple,
                  'time' =&gt; $total_time/$multiple
              );
          }
          return $this; //chainability
      }
  
      /**
       * Returns all the results
       * @return array $results
       */
      public function get_results()
      {
          return $this-&gt;_results;
      }
  }
  
  $benchmark = new Benchmark();
  
  $benchmark-&gt;time( 'Closure test', function () {
      $fn = function($item) { return $item; };
      $fn('test');
  }, true);
  
  $benchmark-&gt;time( 'Anonymous test', function () {
      $fn = create_function( '$item', 'return $item;' );
      $fn('test');
  }, true);
  
  $benchmark-&gt;time( 'Closure direct', function () {
      $test_array = array('test', 'test', 'test', 'test', 'test' );
      $test_array = array_map( function ( $item ) { return $item; }, $test_array );
  }, true);
  
  $benchmark-&gt;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-&gt;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-&gt;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-&gt;get_results());
  </code></pre>
  
  <p>And the results for this code:</p>
  
  <pre><code>array
    0 =&gt; 
      array
        'test' =&gt; string 'Closure test' (length=12)
        'iterations' =&gt; int 10000
        'time' =&gt; float 5.4110765457153E-6
    1 =&gt; 
      array
        'test' =&gt; string 'Anonymous test' (length=14)
        'iterations' =&gt; int 10000
        'time' =&gt; float 1.6784238815308E-5
    2 =&gt; 
      array
        'test' =&gt; string 'Closure direct' (length=14)
        'iterations' =&gt; int 10000
        'time' =&gt; float 1.5178990364075E-5
    3 =&gt; 
      array
        'test' =&gt; string 'Closure stored' (length=14)
        'iterations' =&gt; int 10000
        'time' =&gt; float 1.5463256835938E-5
    4 =&gt; 
      array
        'test' =&gt; string 'Anonymous direct' (length=16)
        'iterations' =&gt; int 10000
        'time' =&gt; float 2.7537250518799E-5
    5 =&gt; 
      array
        'test' =&gt; string 'Anonymous stored' (length=16)
        'iterations' =&gt; int 10000
        'time' =&gt; float 2.8293371200562E-5
  </code></pre>