¿Qué es más rápido: in_array o isset? [cerrado]

96

Esta pregunta es solo para mí, ya que siempre me gusta escribir código optimizado que pueda ejecutarse también en servidores lentos y baratos (o servidores con MUCHO tráfico)

Miré a mi alrededor y no pude encontrar una respuesta. Me preguntaba qué es más rápido entre esos dos ejemplos teniendo en cuenta que las claves de la matriz en mi caso no son importantes (pseudocódigo, naturalmente):

<?php
$a = array();
while($new_val = 'get over 100k email addresses already lowercased'){
    if(!in_array($new_val, $a){
        $a[] = $new_val;
        //do other stuff
    }
}
?>

<?php
$a = array();
while($new_val = 'get over 100k email addresses already lowercased'){
    if(!isset($a[$new_val]){
        $a[$new_val] = true;
        //do other stuff
    }
}
?>

Como el punto de la pregunta no es la colisión de la matriz, me gustaría agregar que si tiene miedo de colisionar las inserciones $a[$new_value], puede usar $a[md5($new_value)]. aún puede causar colisiones, pero evitaría un posible ataque DoS al leer un archivo proporcionado por el usuario ( http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html )

Fabrizio
fuente
3
Si siempre se esfuerza por escribir código optimizado, seguramente está utilizando un generador de perfiles, ¿de vez en cuando?
mario
59
Voto para reabrir. La pregunta está bien formada y las respuestas están respaldadas por hechos y referencias. Mientras que un micro -Optimización, este tipo de preguntas son constructivas .
Jason McCreary
5
@JasonMcCreary segundo; Sólo uno más.
Ja͢ck
7
Esto fue muchos años después, pero ni siquiera lo consideraría una microoptimización. ¡Para grandes conjuntos de datos, puede marcar una gran diferencia!
Robert
2
... esta pregunta me parece "constructiva". Comenzaré otra campaña de reapertura.
mickmackusa

Respuestas:

117

Las respuestas hasta ahora son acertadas. Usar isseten este caso es más rápido porque

  • Utiliza una búsqueda de hash O (1) en la clave, mientras que in_arraydebe verificar cada valor hasta encontrar una coincidencia.
  • Al ser un código de operación, tiene menos gastos generales que llamar a la in_arrayfunción incorporada.

Estos se pueden demostrar usando una matriz con valores (10,000 en la prueba a continuación), lo in_arrayque obliga a realizar más búsquedas.

isset:    0.009623
in_array: 1.738441

Esto se basa en el punto de referencia de Jason al completar algunos valores aleatorios y, ocasionalmente, encontrar un valor que exista en la matriz. Todo al azar, así que ten cuidado con los tiempos que fluctúan.

$a = array();
for ($i = 0; $i < 10000; ++$i) {
    $v = rand(1, 1000000);
    $a[$v] = $v;
}
echo "Size: ", count($a), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    isset($a[rand(1, 1000000)]);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    in_array(rand(1, 1000000), $a);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;
David Harkness
fuente
Sé acerca de los hash, pero me pregunto por qué no se hace algo similar en los valores de las matrices cuando es posible para acelerar las funciones, también reducirá el consumo de memoria si se usan valores similares simplemente agregando un hash adicional en el valor ... ¿correcto?
Fabrizio
3
@Fabrizio: los valores de matriz se pueden duplicar y contener objetos que no se pueden hash. Las claves deben ser únicas y solo pueden ser cadenas y números enteros, lo que las hace fácilmente hash. Si bien puede crear un mapa uno a uno que codifique tanto claves como valores, no es así como funciona la matriz de PHP.
David Harkness
3
En caso de que esté seguro de que su matriz contiene valores únicos, existe otra opción: flip + isset .
Arkadij Kuzhel
vale la pena señalar que un isset invertido es aún más rápido en este ejemplo que in_array: `` `$ start = microtime (true); $ foo = array_flip ($ a); para ($ i = 0; $ i <10000; ++ $ i) {isset ($ foo [rand (1, 1000000)]); } $ total_time = microtime (verdadero) - $ start; echo "Tiempo total (isset invertido):", number_format ($ total_time, 6), PHP_EOL;
Andre Baumeier
@AndreBaumeier Lo que sea más rápido dependerá del tamaño de la matriz y de la cantidad de pruebas que realice. Cambiar una matriz de diez mil elementos para realizar tres pruebas probablemente no sea eficiente.
David Harkness
42

Cuál es más rápido: isset()vsin_array()

isset() es más rápido.

Si bien debería ser obvio, isset()solo prueba un valor único. Considerando in_array()que iterará sobre toda la matriz, probando el valor de cada elemento.

La evaluación comparativa aproximada es bastante fácil de usar microtime().

Resultados:

Total time isset():    0.002857
Total time in_array(): 0.017103

Nota: Los resultados fueron similares independientemente de si existían o no.

Código:

<?php
$a = array();
$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    isset($a['key']);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    in_array('key', $a);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

exit;

Recursos adicionales

Te animo a que también mires:

Jason McCreary
fuente
Buena solucion. Me sorprende que más personas no dividan el tiempo de sus funciones / código más usando microtime()otras herramientas. Increíblemente valioso.
Nickhar
1
La búsqueda de una matriz vacía para la misma clave solo resalta la sobrecarga de llamar a la in_arrayfunción versus usar la función issetintegrada. Esto sería mejor con una matriz que contenga un montón de claves aleatorias y ocasionalmente busque una clave / valor existente.
David Harkness
Utilizo bastante los puntos de referencia y el microtiempo, pero también me di cuenta, mientras estaba probando whiley foreachque en cada actualización obtenía diferentes "ganadores". siempre depende de demasiadas variables del servidor, y lo mejor es iterar una gran cantidad de veces en diferentes momentos y obtener el que gane con más frecuencia, o simplemente saber lo que sucede en segundo plano y saber que será el ganador final pase lo que
pase
@David Harkness, ya has elegido mi respuesta. Si quieres más, párate sobre mis hombros y publica tu propia respuesta. :) No obstante, si la sobrecarga de la función ya es significativamente más cara en relación con isset(), ¿qué le hace pensar que pasarla una matriz más grande la haría más rápida ?
Jason McCreary
1
@Fabrizio - Lea sobre funciones hash y tablas hash .
David Harkness
19

El uso isset()aprovecha una búsqueda más rápida porque usa una tabla hash , evitando la necesidad de O(n)búsquedas.

La clave se hash primero usando la función hash djb para determinar el grupo de claves con hash similar en O(1). Luego, se busca en el depósito de forma iterativa hasta que se encuentra la clave exacta O(n).

Salvo cualquier colisión de hash intencionada , este enfoque produce un rendimiento mucho mejor que in_array().

Tenga en cuenta que cuando se usa isset()de la manera que ha mostrado, pasar los valores finales a otra función requiere usar array_keys()para crear una nueva matriz. Se puede comprometer la memoria almacenando los datos tanto en las claves como en los valores.

Actualizar

Una buena forma de ver cómo las decisiones de diseño de su código afectan el rendimiento en tiempo de ejecución, puede consultar la versión compilada de su script:

echo isset($arr[123])

compiled vars:  !0 = $arr
line     # *  op                           fetch      ext  return  operands
-----------------------------------------------------------------------------
   1     0  >   ZEND_ISSET_ISEMPTY_DIM_OBJ              2000000  ~0      !0, 123
         1      ECHO                                                 ~0
         2    > RETURN                                               null

echo in_array(123, $arr)

compiled vars:  !0 = $arr
line     # *  op                           fetch      ext  return  operands
-----------------------------------------------------------------------------
   1     0  >   SEND_VAL                                             123
         1      SEND_VAR                                             !0
         2      DO_FCALL                                 2  $0      'in_array'
         3      ECHO                                                 $0
         4    > RETURN                                               null

No solo in_array()usa una O(n)búsqueda relativamente ineficiente , también necesita ser llamada como una función ( DO_FCALL) mientras que isset()usa un solo código de operación ( ZEND_ISSET_ISEMPTY_DIM_OBJ) para esto.

Jack
fuente
7

El segundo sería más rápido, ya que solo busca esa clave de matriz específica y no necesita iterar sobre toda la matriz hasta que se encuentre (mirará cada elemento de la matriz si no se encuentra)

Mike Brant
fuente
pero también depende del paradero de una var buscada en el alcance global
el Dude
@ EL2002, ¿podría dar más detalles sobre esa declaración?
Fabrizio
1
Mike, ¿no estaría mirando toda la matriz incluso con el isset()si no se encuentra?
Fabrizio
1
@Fabrizio No, no necesita iterar. Internamente (en C), la matriz PHP es solo una tabla hash. Para buscar un valor de índice único, C simplemente hace un hash de ese valor y busca su ubicación asignada en la memoria. Hay un valor ahí o no lo hay.
Mike Brant
1
@Fabrizio Este artículo proporciona una buena descripción general de cómo PHP representa internamente las matrices en C. nikic.github.com/2012/03/28/…
Mike Brant