property exist content check array php performance reference isset array-key-exists

php - content - ¿Por qué array_key_exists es 1000x más lento que el isset en los arreglos referenciados?



php search in array (3)

Descubrí que array_key_exists es más de 1000x más lento que isset en la verificación si una clave está configurada en una referencia de matriz. ¿Alguien que comprenda cómo se implementa PHP explica por qué esto es cierto?

EDITAR: Agregué otro caso que parece indicar que se requiere una sobrecarga al llamar a las funciones con una referencia.

Ejemplo de referencia

function isset_( $key, array $array ) { return isset( $array[$key] ); } $my_array = array(); $start = microtime( TRUE ); for( $i = 1; $i < 10000; $i++ ) { array_key_exists( $i, $my_array ); $my_array[$i] = 0; } $stop = microtime( TRUE ); print "array_key_exists( /$my_array ) ".($stop-$start).PHP_EOL; unset( $my_array, $my_array_ref, $start, $stop, $i ); $my_array = array(); $start = microtime( TRUE ); for( $i = 1; $i < 10000; $i++ ) { isset( $my_array[$i] ); $my_array[$i] = 0; } $stop = microtime( TRUE ); print "isset( /$my_array ) ".($stop-$start).PHP_EOL; unset( $my_array, $my_array_ref, $start, $stop, $i ); $my_array = array(); $start = microtime( TRUE ); for( $i = 1; $i < 10000; $i++ ) { isset_( $i, $my_array ); $my_array[$i] = 0; } $stop = microtime( TRUE ); print "isset_( /$my_array ) ".($stop-$start).PHP_EOL; unset( $my_array, $my_array_ref, $start, $stop, $i ); $my_array = array(); $my_array_ref = &$my_array; $start = microtime( TRUE ); for( $i = 1; $i < 10000; $i++ ) { array_key_exists( $i, $my_array_ref ); $my_array_ref[$i] = 0; } $stop = microtime( TRUE ); print "array_key_exists( /$my_array_ref ) ".($stop-$start).PHP_EOL; unset( $my_array, $my_array_ref, $start, $stop, $i ); $my_array = array(); $my_array_ref = &$my_array; $start = microtime( TRUE ); for( $i = 1; $i < 10000; $i++ ) { isset( $my_array_ref[$i] ); $my_array_ref[$i] = 0; } $stop = microtime( TRUE ); print "isset( /$my_array_ref ) ".($stop-$start).PHP_EOL; unset( $my_array, $my_array_ref, $start, $stop, $i ); $my_array = array(); $my_array_ref = &$my_array; $start = microtime( TRUE ); for( $i = 1; $i < 10000; $i++ ) { isset_( $i, $my_array_ref ); $my_array_ref[$i] = 0; } $stop = microtime( TRUE ); print "isset_( /$my_array_ref ) ".($stop-$start).PHP_EOL; unset( $my_array, $my_array_ref, $start, $stop, $i );

Salida

array_key_exists( $my_array ) 0.0056459903717 isset( $my_array ) 0.00234198570251 isset_( $my_array ) 0.00539588928223 array_key_exists( $my_array_ref ) 3.64232587814 // <~ what on earth? isset( $my_array_ref ) 0.00222992897034 isset_( $my_array_ref ) 4.12856411934 // <~ what on earth?

Estoy en PHP 5.3.6.

Ejemplo de teclado .


Aquí está la fuente de la función array_key_exists para 5.2.17. Puede ver que incluso si la clave es nula, PHP intenta calcular un hash. Aunque es interesante que si eliminas

// $my_array_ref[$i] = NULL;

entonces se realiza mejor. Debe haber múltiples búsquedas de hash ocurriendo.

/* {{{ proto bool array_key_exists(mixed key, array search) Checks if the given key or index exists in the array */ PHP_FUNCTION(array_key_exists) { zval **key, /* key to check for */ **array; /* array to check in */ if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(ZEND_NUM_ARGS(), &key, &array) == FAILURE) { WRONG_PARAM_COUNT; } if (Z_TYPE_PP(array) != IS_ARRAY && Z_TYPE_PP(array) != IS_OBJECT) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The second argument should be either an array or an object"); RETURN_FALSE; } switch (Z_TYPE_PP(key)) { case IS_STRING: if (zend_symtable_exists(HASH_OF(*array), Z_STRVAL_PP(key), Z_STRLEN_PP(key)+1)) { RETURN_TRUE; } RETURN_FALSE; case IS_LONG: if (zend_hash_index_exists(HASH_OF(*array), Z_LVAL_PP(key))) { RETURN_TRUE; } RETURN_FALSE; case IS_NULL: if (zend_hash_exists(HASH_OF(*array), "", 1)) { RETURN_TRUE; } RETURN_FALSE; default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be either a string or an integer"); RETURN_FALSE; } }


En el trabajo tengo una instancia de máquina virtual de PHP que incluye una extensión PECL llamada VLD. Esto le permite ejecutar código PHP desde la línea de comandos y, en lugar de ejecutarlo, devuelve el código de operación generado.

Es brillante al responder preguntas como esta.

http://pecl.php.net/package/vld

En caso de que siga esta ruta (y si tiene curiosidad acerca de cómo PHP trabaja internamente, creo que debería hacerlo) definitivamente debería instalarlo en una máquina virtual (es decir, no lo instalaría en una máquina). Estoy tratando de desarrollar o desplegar en). Y este es el comando que usarás para hacerlo cantar:

php -d vld.execute=0 -d vld.active=1 -f foo.php

Mirar los códigos de operación le dirá una historia más completa, sin embargo, tengo una conjetura ... La mayoría de los incorporados de PHP hacen una copia de un Array / Objeto y actúan sobre esa copia (y no una copia en escritura). tampoco, una copia inmediata). El ejemplo más conocido de esto es foreach (). Cuando se pasa una matriz a foreach (), PHP en realidad está haciendo una copia de esa matriz y está iterando en la copia. Es por eso que verá un beneficio significativo en el rendimiento al pasar una matriz como referencia a foreach de esta manera:

foreach ($ someReallyBigArray como $ k => & $ v)

Pero este comportamiento, que pasa en una referencia explícita como esa, es exclusivo de foreach (). Así que me sorprendería mucho si hiciera que un array_key_exists () se verificara más rápido.

Ok, volviendo a lo que estaba recibiendo en ...

La mayoría de los incorporados toman una copia de una matriz y actúan sobre esa copia. Voy a aventurar una suposición completamente no calificada de que isset () está altamente optimizado y que una de esas optimizaciones es tal vez no hacer una copia inmediata de un Array cuando se pasa.

Intentaré responder a cualquier otra pregunta que pueda tener, pero probablemente pueda leer muchos de ustedes en Google para "zval_struct" (que es la estructura de datos en las partes internas de PHP que almacena cada variable. Es una estructura en C (piense ... una matriz asociativa) que tiene claves como "valor", "tipo", "refcount".


No array_key_exists, pero la eliminación de la referencia (= NULL) provoca esto. Lo comenté desde tu guión y este es el resultado:

array_key_exists( $my_array ) 0.0059430599212646 isset( $my_array ) 0.0027170181274414 array_key_exists( $my_array_ref ) 0.0038740634918213 isset( $my_array_ref ) 0.0025200843811035

Solo se eliminó la eliminación de la parte array_key_exists( $my_array_ref ) , esta es la parte modificada para referencia:

$my_array = array(); $my_array_ref = &$my_array; $start = microtime( TRUE ); for( $i = 1; $i < 10000; $i++ ) { array_key_exists( $i, $my_array_ref ); // $my_array_ref[$i] = NULL; } $stop = microtime( TRUE ); print "array_key_exists( /$my_array_ref ) ".($stop-$start).PHP_EOL; unset( $my_array, $my_array_ref, $start, $stop, $i );