分配变量与分配 + 添加所需的时间


Time it takes to assign a variable vs. assign + add

<?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的值不会改变并跳过复制,使其更快。