что быстрее: in_array или isset? [закрыто]

97

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

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

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

Фабрицио
источник
3
Если вы всегда стремитесь написать оптимизированный код, вы наверняка используете профилировщик время от времени?
Марио
60
Я голосую за открытие. Вопрос хорошо сформулирован, а ответы подкреплены фактами и ссылками. В то время как микро -оптимизации эти типы вопросов являются конструктивными .
Джейсон МакКрири
5
@JasonMcCreary второй; еще один.
Ja͢ck
7
Это много лет спустя, но я бы даже не стал считать это микрооптимизацией. Для больших наборов данных это может иметь огромное значение !!
Роберт
2
... этот вопрос мне кажется "конструктивным". Я начну еще одну кампанию по повторному открытию.
mickmackusa

Ответы:

118

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

  • Он использует поиск хэша O (1) по ключу, тогда как in_arrayдолжен проверять каждое значение, пока не найдет совпадение.
  • Поскольку он является опкодом, накладные расходы меньше, чем при вызове in_arrayвстроенной функции.

Это можно продемонстрировать с помощью массива со значениями (10 000 в приведенном ниже тесте), что заставит 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;
Дэвид Харкнесс
источник
Я знаю о хэшах, но мне интересно, почему что-то подобное не делается для значений массивов, когда это возможно для ускорения функций, это также уменьшит потребление памяти, если аналогичные значения используются просто путем добавления дополнительного хеширования к значению .. правильно?
Фабрицио
3
@Fabrizio - значения массива могут дублироваться и содержать нехешируемые объекты. Ключи должны быть уникальными и могут быть только строками и целыми числами, что делает их легко хешируемыми. Хотя вы можете создать взаимно однозначную карту, которая хэширует как ключи, так и значения, массив PHP работает не так.
Дэвид Харкнесс
3
Если вы уверены, что ваш массив содержит уникальные значения, есть другой вариант - flip + isset .
Аркадий Кужель
Стоит отметить, что перевернутый isset в этом примере все еще быстрее, чем in_array: `` `$ start = microtime (true); $ foo = array_flip ($ а); for ($ i = 0; $ i <10000; ++ $ i) {isset ($ foo [rand (1, 1000000)]); } $ total_time = microtime (true) - начало $; echo "Общее время (перевернутая установка):", number_format ($ total_time, 6), PHP_EOL;
Андре Бомайер
@AndreBaumeier Что быстрее, будет зависеть от размера массива и количества тестов, которые вы сделаете. Переворачивание массива из десяти тысяч элементов для выполнения трех тестов, вероятно, неэффективно.
Дэвид Харкнесс
42

Что быстрее: 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;

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

Я бы посоветовал вам также взглянуть на:

Джейсон МакКрири
источник
Хорошее решение. Я удивлен, что все больше людей не разделяют время своих функций / кода на использование microtime()других инструментов. Невероятно ценно.
nickhar
1
Поиск того же ключа в пустом массиве только подчеркивает накладные расходы на вызов in_arrayфункции по сравнению с использованием issetвстроенной функции. Было бы лучше с массивом, содержащим кучу случайных ключей и иногда ищущим существующий ключ / значение.
Дэвид Харкнесс
Я довольно часто использую тесты и микропериод, но я также понял, пока я тестировал, whileи foreachчто при каждом обновлении я получал разных «победителей». он всегда зависит от слишком большого количества переменных сервера, и лучше всего перебирать очень большое количество раз в разное время и получать тот, который выигрывает чаще, или просто знать, что происходит в фоновом режиме, и знать, что он будет окончательным победителем несмотря ни на что
Fabrizio
@ Дэвид Харкнесс, вы уже придумали мой ответ. Если вы хотите большего, встаньте мне на плечи и опубликуйте свой ответ. :) Тем не менее, если накладные расходы функции уже значительно дороже по сравнению с isset(), что заставляет вас думать, что передача им большего массива сделает это быстрее ?
Джейсон МакКрири
1
@Fabrizio - Прочтите о хеш-функциях и хэш-таблицах .
Дэвид Харкнесс
19

Использование 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).

Разъем
источник
7

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

Майк Брант
источник
но это также зависит от местонахождения искомой переменной в глобальном масштабе
El Dude
@ EL2002, не могли бы вы уточнить это утверждение?
Fabrizio
1
Майк, разве не стал бы смотреть на весь массив, даже isset()если он не найден?
Fabrizio
1
@Fabrizio Нет, повторять не нужно. Внутренне (в C) массив PHP представляет собой просто хеш-таблицу. Чтобы найти одно значение индекса, C просто создает хэш этого значения и ищет его назначенное место в памяти. Там либо есть значение, либо нет.
Майк Брант,
1
@Fabrizio В этой статье дается хороший обзор того, как массивы внутри C представлены в PHP. nikic.github.com/2012/03/28/…
Майк Брант,