Я обнаружил, что array_key_exists
более 1000 раз медленнее, чем isset
при проверке, установлен ли ключ в ссылке массива. Кто-нибудь, кто понимает, как реализуется PHP, объясняет, почему это так?
EDIT: Я добавил еще одно дело, которое, как представляется, указывает на то, что это накладные расходы, необходимые для вызова функций со ссылкой.
Пример бенчмарка
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 );
Вывод
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?
Я на PHP 5.3.6.
Пример Codepad .
На работе у меня есть экземпляр VM PHP, который включает расширение PECL под названием VLD. Это позволяет вам выполнять PHP-код из командной строки и вместо этого выполнять его, вместо этого он генерирует сгенерированный код операции.
Это блестяще ответить на такие вопросы.
http://pecl.php.net/package/vld
На всякий случай вы идете по этому маршруту (и если вам вообще интересно, как работает PHP внутри, я думаю, вам стоит), вы должны обязательно установить его на виртуальную машину (то есть я бы не установил ее на машине i " m пытается разработать или развернуть). И это команда, которую вы будете использовать, чтобы заставить ее петь:
php -d vld.execute=0 -d vld.active=1 -f foo.php
Глядя на коды операций, вы расскажете вам более полную историю, однако у меня есть предположение …. Большинство встроенных PHP-модулей делают копию массива / объекта и действуют на эту копию (а не на запись-на-запись либо, немедленная копия). Наиболее широко известный пример этого – foreach (). Когда вы передаете массив в foreach (), PHP фактически создает копию этого массива и выполняет итерацию на копии. Вот почему вы увидите значительное преимущество в производительности, передав массив как ссылку в foreach следующим образом:
foreach ($ someReallyBigArray как $ k => & $ v)
Но это поведение – то, что передается в явной ссылке вроде этого, – уникально для foreach (). Поэтому я был бы очень удивлен, если бы он сделал array_key_exists () проверкой быстрее.
Хорошо, к тому, что я получаю ..
Большинство встроенных модулей берут копию массива и действуют на эту копию. Я собираюсь начать совершенно безоговорочное предположение, что isset () сильно оптимизирован и что одна из этих оптимизаций, возможно, не делает немедленную копию массива при его передаче.
Я попытаюсь ответить на любые другие вопросы, которые у вас могут возникнуть, но вы, вероятно, могли бы прочитать много из вас в google для «zval_struct» (которая представляет собой структуру данных внутри внутренних структур PHP, в которой хранится каждая переменная. ассоциативный массив), который имеет такие ключи, как «value», «type», «refcount».
Вот источник функции array_key_exists для 5.2.17. Вы можете видеть, что даже если ключ имеет значение null, PHP пытается вычислить хэш. Хотя интересно, что если вы удалите
// $my_array_ref[$i] = NULL;
то он работает лучше. Должно существовать несколько запросов хеширования.
/* {{{ 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; } }
Не array_key_exists, но удаление ссылки (= NULL) вызывает это. Я прокомментировал это из вашего сценария, и это результат:
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
array_key_exists( $my_array_ref )
только удаление из части array_key_exists( $my_array_ref )
, это модифицированная часть для ссылки:
$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 );