为什么 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_array
的 strict
标志为真,则会发生以下情况:
We call fast_is_identical_function
for each element in the array
第一个fast_is_identical_function
测试每个操作数的类型是不同的(Z_TYPE_P(op1) != Z_TYPE_P(op2)
),希望能早点return false
;这是比较#1。
如果类型相同(在你的测试用例中它们是相同的),我们然后测试(Z_TYPE_P(op1) <= IS_TRUE
;我不知道这是做什么的,但它是 比较 #2.
在两次比较都计算为 false
、we jump into zend_is_identical
之后,我们的第一个函数调用。
zend_is_identical
starts out by again testing Z_TYPE_P(op1) != Z_TYPE_P(op2)
,又一次早早失败的尝试。这是比较#3。
如果类型相同,我们可以通过switch (Z_TYPE_P(op1))
语句下降,比较#4
最后我们进行了比较 Z_LVAL_P(op1) == Z_LVAL_P(op2)
,它实际测试了两个值的相等性,比较 #5.
总的来说,为了测试数组的每个元素是否等于我们正在搜索的值,有 5 次比较和 1 次函数调用。
Non-strict模式
相比之下,整数的 non-strict 流程(实际上是 LONG
s)要简单得多,如下所示:
对于数组中的每个元素,而不是 fast_is_identical_function
,we instead use fast_equal_check_function
。
方法 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);
我们可以看到它...
- 立即测试
op1
的类型是否为LONG
,如果是,则
- 立即测试
op2
的类型是否为LONG
,如果是,则
- 立即returns
Z_LVAL_P(op1) == Z_LVAL_P(op2)
的结果
对于 non-strict 情况总共进行了 3 次简单相等比较和 0 次函数调用,而对于严格情况 至少 5 次比较和 1 次跳转。
这似乎是这样一种情况,即早期优化尝试使严格检查 比比较两个整数的特定 non-strict 情况。
我一直认为 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_array
的 strict
标志为真,则会发生以下情况:
We call
fast_is_identical_function
for each element in the array第一个
fast_is_identical_function
测试每个操作数的类型是不同的(Z_TYPE_P(op1) != Z_TYPE_P(op2)
),希望能早点returnfalse
;这是比较#1。如果类型相同(在你的测试用例中它们是相同的),我们然后测试
(Z_TYPE_P(op1) <= IS_TRUE
;我不知道这是做什么的,但它是 比较 #2.在两次比较都计算为
false
、we jump intozend_is_identical
之后,我们的第一个函数调用。zend_is_identical
starts out by again testingZ_TYPE_P(op1) != Z_TYPE_P(op2)
,又一次早早失败的尝试。这是比较#3。如果类型相同,我们可以通过
switch (Z_TYPE_P(op1))
语句下降,比较#4最后我们进行了比较
Z_LVAL_P(op1) == Z_LVAL_P(op2)
,它实际测试了两个值的相等性,比较 #5.
总的来说,为了测试数组的每个元素是否等于我们正在搜索的值,有 5 次比较和 1 次函数调用。
Non-strict模式
相比之下,整数的 non-strict 流程(实际上是 LONG
s)要简单得多,如下所示:
-
对于数组中的每个元素,
而不是
fast_is_identical_function
,we instead usefast_equal_check_function
。方法
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);
我们可以看到它...
- 立即测试
op1
的类型是否为LONG
,如果是,则 - 立即测试
op2
的类型是否为LONG
,如果是,则 - 立即returns
Z_LVAL_P(op1) == Z_LVAL_P(op2)
的结果
- 立即测试
对于 non-strict 情况总共进行了 3 次简单相等比较和 0 次函数调用,而对于严格情况 至少 5 次比较和 1 次跳转。
这似乎是这样一种情况,即早期优化尝试使严格检查 比比较两个整数的特定 non-strict 情况。