Этот вопрос просто для меня, поскольку мне всегда нравится писать оптимизированный код, который может работать и на дешевых медленных серверах (или на серверах с большим количеством трафика)
Я огляделся, и я не смог найти ответа. Мне было интересно, что быстрее между этими двумя примерами, имея в виду, что ключи массива в моем случае не важны (естественно, псевдокод):
<?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 } } ?>
Поскольку точкой вопроса является не столкновение массива, я хотел бы добавить, что если вы боитесь встречных вставок для $a[$new_value]
, вы можете использовать $a[md5($new_value)]
. он все равно может вызвать конфликты, но может уйти от возможной атаки DoS при чтении из предоставленного пользователем файла ( http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html )
Ответы до сих пор находятся на месте. Использование isset
в этом случае происходит быстрее, потому что
in_array
должен проверять каждое значение, пока не найдет совпадение. in_array
. Их можно продемонстрировать, используя массив со значениями (10000 в приведенном ниже тесте), заставляя in_array
выполнять больше поиска.
isset: 0.009623 in_array: 1.738441
Это основывается на контрольном показателе Джейсона, заполняя некоторые случайные значения и время от времени обнаруживая значение, существующее в массиве. Все случайные, поэтому будьте осторожны, что времена будут колебаться.
$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;
Что быстрее:
isset()
vsin_array()
isset()
работает быстрее.
Хотя это должно быть очевидно, isset()
проверяет только одно значение. В то время как in_array()
будет перебирать весь массив, проверяя значение каждого элемента.
Простой бенчмаркинг довольно просто с использованием microtime()
.
Total time isset(): 0.002857 Total time in_array(): 0.017103
Примечание. Результаты были одинаковыми независимо от того, существовали они или нет.
<?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;
Я бы посоветовал вам также посмотреть:
Использование isset()
использует быстрый поиск, потому что он использует хеш-таблицу , избегая необходимости поиска O(n)
.
Ключ сначала хэширует, используя хэш-функцию djb, чтобы определить ведро аналогично хэшированных клавиш в O(1)
. Затем ведро выполняется итеративно до тех пор, пока не будет найден точный ключ в O(n)
.
Запрещая любые преднамеренные хеш-коллизии , этот подход дает гораздо лучшую производительность, чем in_array()
.
Обратите внимание, что при использовании isset()
в том виде, в котором вы показывали, передача конечных значений другой функции требует использования array_keys()
для создания нового массива. Компромисс памяти может быть сделан путем хранения данных как в ключах, так и в значениях.
Обновить
Хороший способ увидеть, как ваши решения по дизайну кода влияют на производительность выполнения, вы можете проверить скомпилированную версию вашего скрипта:
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
Мало того, что in_array()
использует относительно неэффективный O(n)
поиск, его также нужно вызывать как функцию ( DO_FCALL
), тогда как isset()
использует для этого один код операции ( ZEND_ISSET_ISEMPTY_DIM_OBJ
).
Второй будет быстрее, поскольку он ищет только этот конкретный массив ключей и не нуждается в повторении по всему массиву до его обнаружения (будет рассмотрен каждый элемент массива, если он не найден)