values una funcion filtrar elementos arrayfilter array_walk array_map array aplicar php performance foreach lambda array-map

una - php array_walk use



Rendimiento de foreach, array_map con lambda y array_map con función estática (3)

Es interesante ejecutar este punto de referencia con xdebug desactivado, ya que xdebug agrega bastante sobrecarga, especialmente para las llamadas de función.

Esta es la secuencia de comandos de FGM utilizando 5.6 con xdebug

ForEach : 0.79232501983643 MapClosure: 4.1082420349121 MapNamed : 1.7884571552277

Sin xdebug

ForEach : 0.69830799102783 MapClosure: 0.78584599494934 MapNamed : 0.85125398635864

Aquí solo hay una diferencia muy pequeña entre la versión foreach y la de cierre.

También es interesante agregar una versión con un cierre con un use

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

Para la comparación agrego:

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

Aquí podemos ver que tiene un impacto en la versión de cierre, mientras que la matriz no ha cambiado notablemente.

19/11/2015 También he agregado resultados usando PHP 7 y HHVM para comparar. Las conclusiones son similares, aunque todo es mucho más rápido.

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

¿Cuál es la diferencia de rendimiento (si hay alguna) entre estos tres enfoques, ambos utilizados para transformar una matriz en otra matriz?

  1. Usando foreach
  2. Usando array_map con la función lambda / closure
  3. Usando array_map con función / método "estático"
  4. ¿Hay algún otro enfoque?

Para aclararme, veamos los ejemplos, todos haciendo lo mismo: multiplicar la matriz de números por 10:

$numbers = range(0, 1000);

Para cada

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

Mapa con lambda

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

Mapa con función ''estática'', pasado como referencia de cadena

function tenTimes($number) { return $number * 10; } return array_map(''tenTimes'', $numbers);

¿Hay algún otro enfoque? Estaré feliz de escuchar en realidad todas las diferencias entre los casos de arriba, y las entradas de por qué uno debe ser utilizado en lugar de otros.


Es interesante. Pero obtuve un resultado opuesto con los siguientes códigos, que se simplifican a partir de mis proyectos actuales:

// test a simple array_map in the real world. function test_array_map($data){ return array_map(function($row){ return array( ''productId'' => $row[''id''] + 1, ''productName'' => $row[''name''], ''desc'' => $row[''remark''] ); }, $data); } // Another with local variable $i function test_array_map_use_local($data){ $i = 0; return array_map(function($row) use ($i) { $i++; return array( ''productId'' => $row[''id''] + $i, ''productName'' => $row[''name''], ''desc'' => $row[''remark''] ); }, $data); } // test a simple foreach in the real world function test_foreach($data){ $result = array(); foreach ($data as $row) { $tmp = array(); $tmp[''productId''] = $row[''id''] + 1; $tmp[''productName''] = $row[''name'']; $tmp[''desc''] = $row[''remark'']; $result[] = $tmp; } return $result; } // Another with local variable $i function test_foreach_use_local($data){ $result = array(); $i = 0; foreach ($data as $row) { $i++; $tmp = array(); $tmp[''productId''] = $row[''id''] + $i; $tmp[''productName''] = $row[''name'']; $tmp[''desc''] = $row[''remark'']; $result[] = $tmp; } return $result; }

Aquí están mis datos y códigos de prueba:

$data = array_fill(0, 10000, array( ''id'' => 1, ''name'' => ''test'', ''remark'' => ''ok'' )); $tests = array( ''array_map'' => array(), ''foreach'' => array(), ''array_map_use_local'' => array(), ''foreach_use_local'' => array(), ); for ($i = 0; $i < 100; $i++){ foreach ($tests as $testName => &$records) { $start = microtime(true); call_user_func("test_$testName", $data); $delta = microtime(true) - $start; $records[] = $delta; } } // output result: foreach ($tests as $name => &$records) { printf(''%.4f : %s ''.PHP_EOL, array_sum($records) / count($records), $name); }

El resultado es:

0.0098 : array_map 0.0114 : foreach 0.0114 : array_map_use_local 0.0115 : foreach_use_local

Mis pruebas fueron en el entorno de producción LAMP sin xdebug. I''am wandering xdebug ralentizaría el rendimiento de array_map.


FWIW, acabo de hacer el punto de referencia ya que el cartel no lo hizo. Ejecutando en PHP 5.3.10 + XDebug.

ACTUALIZACIÓN 2015-01-22 compare con la respuesta de mcfedr a continuación para obtener resultados adicionales sin XDebug y una versión PHP más reciente.

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"; }

Obtengo resultados bastante consistentes con números de 1M en una docena de intentos:

  • Foreach: 0.7 segundos
  • Mapa al cierre: 3.4 segundos
  • Mapa en el nombre de la función: 1.2 seg.

Suponiendo que la deslucida velocidad del mapa en el cierre fue causada por el cierre posiblemente evaluado cada vez, también probé así:

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

Pero los resultados son idénticos, lo que confirma que el cierre solo se evalúa una vez.

2014-02-02 ACTUALIZACIÓN: descarga de opcodes

Aquí están los volcados de código de operación para las tres devoluciones de llamada. First useForeach() :

compiled vars: !0 = $numbers, !1 = $result, !2 = $number line # * 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

Entonces useMapClosure()

compiled vars: !0 = $numbers line # * 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

y el cierre que llama:

compiled vars: !0 = $number line # * 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

luego la función useMapNamed() :

compiled vars: !0 = $numbers line # * 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

y la función nombrada a la que llama, _tenTimes() :

compiled vars: !0 = $number line # * 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