Почему строгий режим in_array для целых чисел медленнее, чем строгий режим? - PullRequest
9 голосов
/ 03 июля 2019

Я всегда думал, что in_array строгий режим будет быстрее или, по крайней мере, с той же скоростью, что и не строгий режим. Но после некоторых тестов я заметил огромную разницу во времени выполнения между ними при поиске целых чисел. Тесты строк и массивов показывают, что строгий режим работает быстрее. Почему?

Тестовый код - (PHP 7.2.1):

<?php

$array = array_fill(0, 10000, 12345);

for ($i=0; $i<100000; $i++) {
    in_array($i, $array, true);
}

time php test.php

php -c test.php 12.98s пользователь 0.04s система 98% ЦП 13.234 всего


<?php

$array = array_fill(0, 10000, 12345);

for ($i=0; $i<100000; $i++) {
    in_array($i, $array, false);
}

time php test.php

php -c test.php 6.44s пользователь 0.04s система 99% процессор 6.522 всего

Ответы [ 2 ]

4 голосов
/ 03 июля 2019

Я могу предложить небольшое представление о трассировке источника C для in_array.

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

Строгий режим

В случае, если флаг strict для in_array имеет значение true, происходит следующее:

  1. Мы вызываем fast_is_identical_function для каждого элемента в массиве

  2. fast_is_identical_function первый проверяет, что типы каждого операнда различны (Z_TYPE_P(op1) != Z_TYPE_P(op2)) в надежде на возможность вернуть false рано; это сравнение # 1 .

  3. Если типы совпадают (они есть в вашем тестовом примере), тогда мы тестируем (Z_TYPE_P(op1) <= IS_TRUE; Я понятия не имею, что это делает, но это сравнение # 2 .

  4. После того, как оба сравнения оцениваются как false, , мы переходим к zend_is_identical, нашему первому вызову функции.

  5. zend_is_identical начинается с снова тестирование Z_TYPE_P(op1) != Z_TYPE_P(op2), еще одна попытка рано провалиться. Это сравнение # 3 .

  6. Если типы совпадают, мы можем спуститься через оператор switch (Z_TYPE_P(op1)), сравнение # 4

  7. Наконец мы достигаем сравнения Z_LVAL_P(op1) == Z_LVAL_P(op2), которое фактически проверяет равенство двух значений, сравнение # 5 .

Всего, чтобы проверить, равен ли каждый элемент массива искомому значению, есть 5 сравнений и 1 вызов функции.

Нестрогий режим

Для сравнения, строгий поток для целых чисел (на самом деле LONG s) намного проще, как показано ниже:

  1. Вместо fast_is_identical_function, вместо этого мы используем fast_equal_check_function для каждого элемента в массиве.

  2. Метод fast_equal_check_function запускает гораздо более сложный процесс сравнения двух значений со всеми видами логики приведения типов. Однако сам тест first , который он действительно выполняет, оптимизирован для целых чисел следующим образом:

    if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
        if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
            return Z_LVAL_P(op1) == Z_LVAL_P(op2);
    

    Мы видим, что это ...

    1. немедленно проверяет, является ли тип op1 LONG, то есть
    2. немедленно проверяет, является ли тип op2 LONG, то есть
    3. немедленно возвращает результат Z_LVAL_P(op1) == Z_LVAL_P(op2)

Всего 3 простых сравнения на равенство и 0 вызовов функций для нестрогого случая, против не менее 5 сравнений и 1 переход для строгого случая.

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

1 голос
/ 03 июля 2019

Похоже, что-то связано с типом элемента в иголке и / или стоге сена, обратите внимание:

PHP 7.3.5 от http://sandbox.onlinephpfunctions.com/

$iterations = 10000000;
$needle = false;
$haystack = [ true ];

$start = microtime( true );
for( $i = 0; $i < $iterations; ++$i )
{
    in_array( $needle, $haystack, true );
}
echo ( microtime( true ) - $start ).' strict'.PHP_EOL;

$start = microtime( true );
for( $i = 0; $i < $iterations; ++$i )
{
    in_array( $needle, $haystack, false );
}
echo ( microtime( true ) - $start ).' not strict';

производит:

0.29996585845947 strict
0.40397191047668 not strict

но если мы используем:

$needle = 1;
$haystack = [ 2 ];

тогда получим:

0.34480714797974 strict
0.28275084495544 not strict

Тем не менее, PHP 5.6.29 производит незначительное несоответствие, и многократный запуск одного и того же теста может поставить строгий перед нестрогим и наоборот.

...