为什么 in_array 整数的严格模式比非严格模式慢?

Why is in_array strict mode on integers slower than non-strict mode?

我一直认为 in_array 严格模式会比非严格模式更快或至少与非严格模式相同。但是在一些基准测试之后,我注意到在搜索整数时它们之间的执行时间存在巨大差异。字符串和数组测试表明严格模式更快。为什么?

Test code - (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 user 0.04s system 98% cpu 13.234 total


<?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 user 0.04s system 99% cpu 6.522 total

好像跟针里的元素类型有关系and/or大海捞针,观察:

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 产生的差异可以忽略不计,运行 多次相同的测试可以将严格置于 non-strict 之前,反之亦然。

我可以通过跟踪 in_array 的 C 源代码提供一些小见解。

事实证明,比较整数时,non-strict模式达到实际相等性检查的路径比严格模式涉及的操作更少。

严格模式

如果 in_arraystrict 标志为真,则会发生以下情况:

  1. We call fast_is_identical_function for each element in the array

  2. 第一个fast_is_identical_function 测试每个操作数的类型是不同的(Z_TYPE_P(op1) != Z_TYPE_P(op2)),希望能早点return false;这是比较#1

  3. 如果类型相同(在你的测试用例中它们是相同的),我们然后测试(Z_TYPE_P(op1) <= IS_TRUE;我不知道这是做什么的,但它是 比较 #2.

  4. 在两次比较都计算为 falsewe jump into zend_is_identical 之后,我们的第一个函数调用。

  5. zend_is_identical starts out by again testing 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 次函数调用。

Non-strict模式

相比之下,整数的 non-strict 流程(实际上是 LONGs)要简单得多,如下所示:

    对于数组中的每个元素,
  1. 而不是 fast_is_identical_functionwe instead use fast_equal_check_function

  2. 方法 fast_equal_check_function 启动了一个更复杂的过程,将两个值与各种 type-casting 逻辑进行比较。然而,第一个测试恰好针对整数进行了优化,如下所示:

    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. 立即returns Z_LVAL_P(op1) == Z_LVAL_P(op2)
    4. 的结果

对于 non-strict 情况总共进行了 3 次简单相等比较和 0 次函数调用,而对于严格情况 至少 5 次比较和 1 次跳转。

这似乎是这样一种情况,即早期优化尝试使严格检查 比比较两个整数的特定 non-strict 情况。