Что быстрее: in_array или isset?

Этот вопрос просто для меня, так как мне всегда нравится писать оптимизированный код, который может работать и на дешевых медленных серверах (или на серверах с большим количеством трафика)

Я огляделся, и я не смог найти ответ. Мне было интересно, что быстрее между этими двумя примерами, имея в виду, что ключи массива в моем случае не важны (естественно, псевдокод):

<?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)

Ответ 1

Ответы до сих пор находятся на месте. Использование isset в этом случае происходит быстрее, потому что

  • Он использует поиск хеша O (1) в ключе, тогда как 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;

Ответ 2

Это быстрее: isset() vs in_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;

Дополнительные ресурсы

Я бы посоветовал вам также посмотреть:

Ответ 3

Использование isset() использует более быстрый поиск, потому что он использует таблицу хешей , избегая необходимости поиска O(n).

Ключ сначала хэшируется с помощью функции хеша djb, чтобы определить ведро аналогичных хэшированных клавиш в O(1). Затем ведро выполняется итеративно до тех пор, пока точный ключ не будет найден в O(n).

Запрет любых преднамеренных хеш-коллизий, этот подход дает гораздо лучшую производительность, чем in_array().

Обратите внимание, что при использовании isset() в том виде, который вы указали, передача конечных значений другой функции требует использования array_keys() для создания нового массива. Компромисс памяти может быть сделан путем хранения данных как в ключах, так и в значениях.

Обновление

Хороший способ увидеть, как ваши решения по дизайну кода влияют на производительность выполнения, вы можете проверить скомпилированную версию вашего 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

Не только in_array() использует относительно неэффективный поиск O(n), он также должен быть вызван как функция (DO_FCALL), тогда как isset() использует один код операции (ZEND_ISSET_ISEMPTY_DIM_OBJ) для этого.

Ответ 4

Второй будет быстрее, поскольку он ищет только этот конкретный массив ключей и не нуждается в повторении по всему массиву до его обнаружения (будет выглядеть каждый элемент массива, если он не найден)