tag recorrer name multidimensional index for asociativo array php arrays performance foreach for-loop

recorrer - php for



Rendimiento de FOR vs FOREACH en PHP (4)

Creo, pero no estoy seguro: el bucle for toma dos operaciones para verificar e incrementar los valores. foreach carga los datos en la memoria y luego iterará todos los valores.

Antes que nada, entiendo que en el 90% de las aplicaciones, la diferencia de rendimiento es completamente irrelevante, pero solo necesito saber cuál es la construcción más rápida. Eso y ...

La información actualmente disponible en ellos en la red es confusa. Mucha gente dice que foreach es malo, pero técnicamente debería ser más rápido, ya que se supone que simplifica la escritura de un recorrido de matriz mediante iteradores. Iteradores, que se supone que son más rápidos, pero en PHP también son aparentemente lentos (¿no es algo PHP?). Estoy hablando de las funciones de matriz: next () prev () reset () etc., si son funciones uniformes y no una de esas funciones de lenguaje PHP que parecen funciones.

Para reducir esto un poco : no me interesa trazar las matrices en pasos de más de 1 (tampoco pasos negativos, es decir, iteración inversa). Tampoco estoy interesado en un recorrido hacia y desde puntos arbitrarios, solo de 0 a la longitud. Tampoco veo la manipulación de matrices con más de 1000 claves que ocurren de manera regular, pero sí veo una matriz que se cruza varias veces en la lógica de una aplicación. También en cuanto a operaciones, en gran parte solo manipulación de cuerdas y eco.

Aquí hay algunos sitios de referencia:
http://www.phpbench.com/
http://www.php.lt/benchmark/phpbench.php

Lo que escucho en todas partes:

  • foreach es lento, y for tanto for / while es más rápido
  • PHPs foreach copia la matriz sobre la que itera; para hacerlo más rápido, necesitas usar referencias
  • código como este: $key = array_keys($aHash); $size = sizeOf($key);
    for ($i=0; $i < $size; $i++)
    $key = array_keys($aHash); $size = sizeOf($key);
    for ($i=0; $i < $size; $i++)
    $key = array_keys($aHash); $size = sizeOf($key);
    for ($i=0; $i < $size; $i++)
    es más rápido que un foreach

Aquí está mi problema. Escribí este script de prueba: http://pastebin.com/1ZgK07US y no importa cuántas veces ejecute el script, obtengo algo como esto:

foreach 1.1438131332397 foreach (using reference) 1.2919359207153 for 1.4262869358063 foreach (hash table) 1.5696921348572 for (hash table) 2.4778981208801

En breve:

  • foreach es más rápido que foreach con referencia
  • foreach es más rápido que for
  • foreach es más rápido que para una tabla hash

¿Alguien puede explicar?

  1. ¿Estoy haciendo algo mal?
  2. ¿La cosa de referencia foreach de PHP realmente está marcando la diferencia? Quiero decir por qué no lo copiaría si pasas por referencia?
  3. ¿Cuál es el código iterativo equivalente para la declaración foreach? He visto algunos en la red, pero cada vez que los pruebo el tiempo está muy lejos; También probé algunas construcciones simples de iteradores, pero nunca obtuve resultados decentes. ¿Los iteradores de matriz en PHP son terribles?
  4. ¿Hay formas / métodos / construcciones más rápidos para iterar a través de una matriz que no sea FOR / FOREACH (y WHILE)?

Versión PHP 5.3.0

Editar: Respuesta Con la ayuda de la gente de aquí, pude juntar las respuestas a todas las preguntas. Los resumiré aquí:
  1. "¿Estoy haciendo algo mal?" El consenso parece ser: sí, no puedo usar el eco en los puntos de referencia. Personalmente, todavía no veo cómo el eco es alguna función con tiempo de ejecución aleatorio o cómo cualquier otra función es de alguna manera diferente; eso y la capacidad de ese script para generar exactamente los mismos resultados de foreach mejor que todo es difícil para explicar solo "estás usando eco" (bueno, ¿qué debería haber estado usando?). Sin embargo, concedo que la prueba debe hacerse con algo mejor; aunque un compromiso ideal no viene a la mente.
  2. "¿Es la cosa de referencia foreach de PHP realmente haciendo una diferencia? Quiero decir por qué no la copia si pasa por referencia?" ircmaxell muestra que sí, parece que probar más pruebas en la mayoría de los casos la referencia debería ser más rápida, aunque dado mi fragmento de código anterior, definitivamente no significa todo. Acepto que es probable que este problema no sea demasiado intuitivo como para molestarlo a ese nivel y que requiera algo extremo como la descompilación para determinar realmente cuál es mejor para cada situación.
  3. "¿Cuál es el código iterativo equivalente para la declaración foreach? He visto algunos en la red, pero cada vez que los pruebo el tiempo está muy lejos, también he probado algunas construcciones de iteradores simples, pero nunca parecen obtener resultados decentes - ¿Los iteradores de matriz en PHP son terribles? ircmaxell proporcionó la respuesta a continuación; aunque el código solo puede ser válido para la versión de PHP> = 5
  4. "¿Hay formas / métodos / construcciones más rápidos para iterar a través de una matriz que no sea FOR / FOREACH (y WHILE)?" Gracias a Gordon por la respuesta. El uso de nuevos tipos de datos en PHP5 debería proporcionar un aumento del rendimiento o de memoria (cualquiera de los cuales podría ser conveniente dependiendo de su situación). Si bien es cierto que muchos de los nuevos tipos de matriz no parecen ser mejores que array (), la splpriorityqueue y splobjectstorage parecen ser mucho más rápidos. Enlace proporcionado por Gordon: http://matthewturland.com/2010/05/20/new-spl-features-in-php-5-3/

Gracias a todos los que trataron de ayudar.

Probablemente me aferraré a foreach (la versión sin referencia) para cualquier cruce simple.


Mi opinión personal es usar lo que tiene sentido en el contexto. Personalmente, casi nunca uso for atravesar array. Lo uso para otros tipos de iteración, pero foreach es demasiado fácil ... La diferencia de tiempo va a ser mínima en la mayoría de los casos.

Lo más importante a tener en cuenta es:

for ($i = 0; $i < count($array); $i++) {

Es un ciclo costoso, ya que llamadas cuentan en cada iteración. Mientras no hagas eso, no creo que realmente importe ...

En cuanto a la referencia que hace la diferencia, PHP usa copy-on-write, por lo que si no escribe en la matriz, habrá relativamente poca sobrecarga al hacer bucles. Sin embargo, si comienzas a modificar la matriz dentro de la matriz, ahí es donde comenzarás a ver las diferencias entre ellos (ya que uno tendrá que copiar toda la matriz, y la referencia puede modificarse en línea) ...

En cuanto a los iteradores, foreach es equivalente a:

$it->rewind(); while ($it->valid()) { $key = $it->key(); // If using the $key => $value syntax $value = $it->current(); // Contents of loop in here $it->next(); }

En cuanto a que haya formas más rápidas de iterar, realmente depende del problema. Pero realmente necesito preguntar, ¿por qué? Entiendo que quiero hacer las cosas más eficientes, pero creo que estás perdiendo el tiempo para una micro-optimización. Recuerde, la Premature Optimization Is The Root Of All Evil ...

Editar: Basado en el comentario, decidí hacer una prueba rápida de referencia ...

$a = array(); for ($i = 0; $i < 10000; $i++) { $a[] = $i; } $start = microtime(true); foreach ($a as $k => $v) { $a[$k] = $v + 1; } echo "Completed in ", microtime(true) - $start, " Seconds/n"; $start = microtime(true); foreach ($a as $k => &$v) { $v = $v + 1; } echo "Completed in ", microtime(true) - $start, " Seconds/n"; $start = microtime(true); foreach ($a as $k => $v) {} echo "Completed in ", microtime(true) - $start, " Seconds/n"; $start = microtime(true); foreach ($a as $k => &$v) {} echo "Completed in ", microtime(true) - $start, " Seconds/n";

Y los resultados:

Completed in 0.0073502063751221 Seconds Completed in 0.0019769668579102 Seconds Completed in 0.0011849403381348 Seconds Completed in 0.00111985206604 Seconds

Entonces, si está modificando la matriz en el ciclo, es mucho más rápido usar referencias ...

Y la sobrecarga para solo la referencia es en realidad menor que copiar la matriz (esto es en 5.3.2) ... Entonces aparece (en 5.3.2 al menos) como si las referencias fueran significativamente más rápidas ...


No estoy seguro de que esto sea tan sorprendente. La mayoría de las personas que codifican en PHP no están muy familiarizadas con lo que PHP está haciendo realmente al descubierto. Declararé algunas cosas, que serán ciertas la mayor parte del tiempo:

  1. Si no está modificando la variable, el valor by es más rápido en PHP. Esto se debe a que su referencia se cuenta de todos modos y el valor por defecto le da menos que hacer. Sabe que en el momento en que modifiques esa ZVAL (la estructura de datos interna de PHP para la mayoría de los tipos), tendrá que romperla de manera directa (cópiala y olvídate de la otra ZVAL). Pero nunca lo modificas, entonces no importa. Las referencias lo hacen más complicado con más contabilidad que tiene que hacer para saber qué hacer cuando modificas la variable. Entonces, si eres de solo lectura, paradójicamente es mejor no decir eso con el &. Lo sé, es contrario a la intuición, pero también es cierto.

  2. Foreach no es lento. Y para la iteración simple, la condición contra la que está probando, "estoy al final de esta matriz", se hace usando código nativo, no códigos de operación de PHP. Incluso si se trata de códigos de operación en caché APC, es aún más lento que un montón de operaciones nativas realizadas en el metal desnudo.

  3. Usar un bucle for "for ($ i = 0; $ i <count ($ x); $ i ++) es lento debido al conteo (), y la falta de habilidad de PHP (o realmente cualquier lenguaje interpretado) para evaluar en el análisis hora si algo modifica la matriz. Esto evita que evalúe la cuenta una vez.

  4. Pero incluso una vez que lo arregles con "$ c = count ($ x); for ($ i = 0; $ i <$ c; $ i ++) el $ i <$ c es un montón de códigos de operación Zend en el mejor de los casos, $ i ++. En el transcurso de 100000 iteraciones, esto puede importar. Foreach sabe a nivel nativo qué hacer. No se necesitan códigos de operación de PHP para probar la condición "estoy al final de esta matriz".

  5. ¿Qué pasa con la vieja escuela? "While" (lista ("cosas") Bueno, usar cada (), actual (), etc. va a involucrar al menos una llamada de función, que no es lenta, pero no gratuita. son códigos de operación de PHP otra vez! Así que + + + cada uno tiene sus costos también.

Por estas razones, foreach es comprensiblemente la mejor opción para la iteración simple.

Y no lo olvide, también es el más fácil de leer, por lo que es ganar-ganar.


Una cosa a tener en cuenta en los puntos de referencia (especialmente phpbench.com), es que aunque los números son sólidos, las pruebas no lo son. Muchas de las pruebas en phpbench.com están haciendo cosas triviales y abusan de la capacidad de PHP de almacenar en caché las búsquedas de matriz para sesgar puntos de referencia o en el caso de iterar sobre una matriz no lo prueba en casos del mundo real (nadie escribe vacío para bucles). He hecho mis propios puntos de referencia que he encontrado son bastante reflexivos de los resultados del mundo real y siempre muestran la sintaxis foreach nativa del idioma para que todo salga en la parte superior (sorpresa, sorpresa).

//make a nicely random array $aHash1 = range( 0, 999999 ); $aHash2 = range( 0, 999999 ); shuffle( $aHash1 ); shuffle( $aHash2 ); $aHash = array_combine( $aHash1, $aHash2 ); $start1 = microtime(true); foreach($aHash as $key=>$val) $aHash[$key]++; $end1 = microtime(true); $start2 = microtime(true); while(list($key) = each($aHash)) $aHash[$key]++; $end2 = microtime(true); $start3 = microtime(true); $key = array_keys($aHash); $size = sizeOf($key); for ($i=0; $i<$size; $i++) $aHash[$key[$i]]++; $end3 = microtime(true); $start4 = microtime(true); foreach($aHash as &$val) $val++; $end4 = microtime(true); echo "foreach ".($end1 - $start1)."/n"; //foreach 0.947947025299 echo "while ".($end2 - $start2)."/n"; //while 0.847212076187 echo "for ".($end3 - $start3)."/n"; //for 0.439476966858 echo "foreach ref ".($end4 - $start4)."/n"; //foreach ref 0.0886030197144 //For these tests we MUST do an array lookup, //since that is normally the *point* of iteration //i''m also calling noop on it so that PHP doesn''t //optimize out the loopup. function noop( $value ) {} //Create an array of increasing indexes, w/ random values $bHash = range( 0, 999999 ); shuffle( $bHash ); $bstart1 = microtime(true); for($i = 0; $i < 1000000; ++$i) noop( $bHash[$i] ); $bend1 = microtime(true); $bstart2 = microtime(true); $i = 0; while($i < 1000000) { noop( $bHash[$i] ); ++$i; } $bend2 = microtime(true); $bstart3 = microtime(true); foreach( $bHash as $value ) { noop( $value ); } $bend3 = microtime(true); echo "for ".($bend1 - $bstart1)."/n"; //for 0.397135972977 echo "while ".($bend2 - $bstart2)."/n"; //while 0.364789962769 echo "foreach ".($bend3 - $bstart3)."/n"; //foreach 0.346374034882