这只是一个有根据的猜测,基于查看 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 万次迭代中,它也只能累积到 0.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
不会改变,跳过复制,速度更快。