Performance of foreach, array_map with lambda and array_map with static function Performance of foreach, array_map with lambda and array_map with static function php php

Performance of foreach, array_map with lambda and array_map with static function


Its interesting to run this benchmark with xdebug disabled, as xdebug adds quite a lot of overhead, esp to function calls.

This is FGM's script run using 5.6With xdebug

ForEach   : 0.79232501983643MapClosure: 4.1082420349121MapNamed  : 1.7884571552277

Without xdebug

ForEach   : 0.69830799102783MapClosure: 0.78584599494934MapNamed  : 0.85125398635864

Here there is only a very small difference between the foreach and closure version.

Its also interesting to add a version with a closure with a use

function useMapClosureI($numbers) {  $i = 10;  return array_map(function($number) use ($i) {      return $number * $i++;  }, $numbers);}

For comparison I add:

function useForEachI($numbers)  {  $result = array();  $i = 10;  foreach ($numbers as $number) {    $result[] = $number * $i++;  }  return $result;}

Here we can see it makes an impact on the closure version, whereas the array hasn't noticeably changed.

19/11/2015 I have also now added results using PHP 7 and HHVM for comparison. The conclusions are similar, though everything is much faster.

PHP 5.6ForEach    : 0.57499806880951MapClosure : 0.59327731132507MapNamed   : 0.69694859981537MapClosureI: 0.73265469074249ForEachI   : 0.60068697929382PHP 7ForEach    : 0.11297199726105MapClosure : 0.16404168605804MapNamed   : 0.11067249774933MapClosureI: 0.19481580257416ForEachI   : 0.10989861488342HHVMForEach    : 0.090071058273315MapClosure : 0.10432276725769MapNamed   : 0.1091267824173MapClosureI: 0.11197068691254ForEachI   : 0.092114186286926


FWIW, I just did the benchmark since poster didn't do it. Running on PHP 5.3.10 + XDebug.

UPDATE 2015-01-22 compare with mcfedr's answer below for additional results without XDebug and a more recent PHP version.

function lap($func) {  $t0 = microtime(1);  $numbers = range(0, 1000000);  $ret = $func($numbers);  $t1 = microtime(1);  return array($t1 - $t0, $ret);}function useForeach($numbers)  {  $result = array();  foreach ($numbers as $number) {      $result[] = $number * 10;  }  return $result;}function useMapClosure($numbers) {  return array_map(function($number) {      return $number * 10;  }, $numbers);}function _tenTimes($number) {    return $number * 10;}function useMapNamed($numbers) {  return array_map('_tenTimes', $numbers);}foreach (array('Foreach', 'MapClosure', 'MapNamed') as $callback) {  list($delay,) = lap("use$callback");  echo "$callback: $delay\n";}

I get pretty consistent results with 1M numbers across a dozen attempts:

  • Foreach: 0.7 sec
  • Map on closure: 3.4 sec
  • Map on function name: 1.2 sec.

Supposing the lackluster speed of the map on closure was caused by the closure possibly being evaluated each time, I also tested like this:

function useMapClosure($numbers) {  $closure = function($number) {    return $number * 10;  };  return array_map($closure, $numbers);}

But the results are identical, confirming that the closure is only evaluated once.

2014-02-02 UPDATE: opcodes dump

Here are the opcode dumps for the three callbacks. First useForeach():

compiled vars:  !0 = $numbers, !1 = $result, !2 = $numberline     # *  op                           fetch          ext  return  operands---------------------------------------------------------------------------------  10     0  >   EXT_NOP                                                           1      RECV                                                     1  11     2      EXT_STMT                                                          3      INIT_ARRAY                                       ~0               4      ASSIGN                                                   !1, ~0  12     5      EXT_STMT                                                          6    > FE_RESET                                         $2      !0, ->15         7  > > FE_FETCH                                         $3      $2, ->15         8  >   OP_DATA                                                           9      ASSIGN                                                   !2, $3  13    10      EXT_STMT                                                         11      MUL                                              ~6      !2, 10        12      ASSIGN_DIM                                               !1        13      OP_DATA                                                  ~6, $7  14    14    > JMP                                                      ->7        15  >   SWITCH_FREE                                              $2  15    16      EXT_STMT                                                         17    > RETURN                                                   !1  16    18*     EXT_STMT                                                         19*   > RETURN                                                   null

Then the useMapClosure()

compiled vars:  !0 = $numbersline     # *  op                           fetch          ext  return  operands---------------------------------------------------------------------------------  18     0  >   EXT_NOP                                                           1      RECV                                                     1  19     2      EXT_STMT                                                          3      EXT_FCALL_BEGIN                                                   4      DECLARE_LAMBDA_FUNCTION                                  '%00%7Bclosure%7D%2Ftmp%2Flap.php0x7f7fc1424173'  21     5      SEND_VAL                                                 ~0         6      SEND_VAR                                                 !0         7      DO_FCALL                                      2  $1      'array_map'         8      EXT_FCALL_END                                                     9    > RETURN                                                   $1  22    10*     EXT_STMT                                                         11*   > RETURN                                                   null

and the closure it calls:

compiled vars:  !0 = $numberline     # *  op                           fetch          ext  return  operands---------------------------------------------------------------------------------  19     0  >   EXT_NOP                                                           1      RECV                                                     1  20     2      EXT_STMT                                                          3      MUL                                              ~0      !0, 10         4    > RETURN                                                   ~0  21     5*     EXT_STMT                                                          6*   > RETURN                                                   null

then the useMapNamed() function:

compiled vars:  !0 = $numbersline     # *  op                           fetch          ext  return  operands---------------------------------------------------------------------------------  28     0  >   EXT_NOP                                                           1      RECV                                                     1  29     2      EXT_STMT                                                          3      EXT_FCALL_BEGIN                                                   4      SEND_VAL                                                 '_tenTimes'         5      SEND_VAR                                                 !0         6      DO_FCALL                                      2  $0      'array_map'         7      EXT_FCALL_END                                                     8    > RETURN                                                   $0  30     9*     EXT_STMT                                                         10*   > RETURN                                                   null

and the named function it calls, _tenTimes():

compiled vars:  !0 = $numberline     # *  op                           fetch          ext  return  operands---------------------------------------------------------------------------------  24     0  >   EXT_NOP                                                           1      RECV                                                     1  25     2      EXT_STMT                                                          3      MUL                                              ~0      !0, 10         4    > RETURN                                                   ~0  26     5*     EXT_STMT                                                          6*   > RETURN                                                   null


Here are some updated tests for the current PHP 8 (RC2) version.Also added short closures

PHP 8.0 RC2Foreach:         0.093745978673299MapClosure:      0.096948345502218MapShortClosure: 0.096264243125916MapNamed:        0.091399153073629MapClosureI:     0.11352666219076ForEachI:        0.097501540184021