<?php
$a = microtime(true);
$num = 0;
for($i=0;$i<10000000;$i++)
{
$num = $i;
}
$b= microtime(true);
echo $b-$a;
?>
我在 Ubuntu 12.10 和 Apache 2 上运行它会给我大约。50秒...当我运行一百万次作业时..但是...
相同的代码,而不是$num = $i
...我去...
$num = $i + 10;
,现在执行所需的时间几乎减少了 1.5 倍..始终如一地约为 .36..
为什么简单的作业需要更多,而一项作业并在其上添加 10 分......花费更少的时间!
我绝不是专家,但这是我的发现:
$s = microtime(true);
for($i=0;$i<100000000;$i++) $tmp = $i;
$t = microtime(true);
for($i=0;$i<100000000;$i++) $tmp = $i+10;
$u = microtime(true);
echo ($t-$s).chr(10).($u-$t);
结果:
9.9528648853302
9.0821340084076
另一方面,使用常量值进行分配测试:
$x = 0;
$s = microtime(true);
for($i=0;$i<100000000;$i++) $tmp = $x;
$t = microtime(true);
for($i=0;$i<100000000;$i++) $tmp = $x+10;
$u = microtime(true);
echo ($t-$s).chr(10).($u-$t);
结果:
6.1365358829498
9.3231790065765
这让我相信答案与操作码缓存有关。老实说,我无法告诉您它有什么不同,但正如您所看到的,为赋值使用常量值会产生巨大的差异。
这只是一个有根据的猜测,基于查看 Github 上最新的 php 源代码,但我想说这种差异是由于解释器源代码中的函数调用开销造成的。
$tmp = $i;
编译为单个操作码ASSIGN !2, !1;
,将一个命名变量的值复制到另一个命名变量。在源代码中,关键部分如下所示:
if (EXPECTED(Z_TYPE_P(variable_ptr) <= IS_BOOL)) {
/* nothing to destroy */
ZVAL_COPY_VALUE(variable_ptr, value);
zendi_zval_copy_ctor(*variable_ptr);
}
$tmp = $i + 10;
编译为两个操作码ADD ~8 !1, 10; ASSIGN !2, ~8;
,这将创建一个临时变量~8
并将其值分配给命名变量。在源代码中,关键部分如下所示:
if (EXPECTED(Z_TYPE_P(variable_ptr) <= IS_BOOL)) {
/* nothing to destroy */
ZVAL_COPY_VALUE(variable_ptr, value);
}
请注意,在第一种情况下,有一个额外的函数调用来zendi_zval_copy_ctor()
。该函数根据需要执行一些簿记(例如,如果原始变量是资源,则需要确保在这个新变量消失之前不会释放资源,等等(。对于基元类型(如数字(,无需执行任何操作,但函数调用本身会引入一些开销,这会累积超过 1000 万次测试迭代。您应该注意,此开销通常可以忽略不计,因为即使在 1000 万次迭代中,它也只能累积到 .14 秒。
@Kolink关于常数更快的观察也可以在同一函数中得到回答。它包括一个检查,以避免在新值与旧值相同时进行冗余复制:
if (EXPECTED(variable_ptr != value)) {
copy_value:
// the same code that handles `$tmp = $i` above
if (EXPECTED(Z_TYPE_P(variable_ptr) <= IS_BOOL)) {
/* nothing to destroy */
ZVAL_COPY_VALUE(variable_ptr, value);
zendi_zval_copy_ctor(*variable_ptr);
} else {
/* irrelevant to the question */
}
}
所以只有$tmp = $x
的第一个赋值会复制$x
的值,下面的赋值看到$tmp
的值不会改变并跳过复制,使其更快。