Zend Engine中的函数内联
现代PHP很快!它具有多项性能功能,例如OPCache,JIT和其他编译阶段的改进,可针对许多PHP应用程序进行智能优化。
检查OPCode是确保PHP可以进行最佳优化的简便方法。使用列出的OPCode,可以更清楚地了解给定的PHP代码段是否采用了执行预期任务所需的最短数量的OPCode。
目前,PHP有30多个这样的函数,它们使用特殊的OPCode或内联以提高性能。
展示这种效果的一个例子是 strlen
函数。它返回给定字符串的长度,PHP尝试抢先优化。
if (strlen('Test') < 2) {
echo "Test";
}
在此代码段中,该strlen
函数在静态字符串文字上调用,并且PHP可以完全消除此块,因为Test
字符串的长度是固定的,并且比较值也是静态值。
使用OPCode dump可以更好地揭示这一点。
优化之前
php -d opcache.opt_debug_level=0x10000 test.php
0000 JMPZ bool(false) 0002
0001 ECHO string("Test")
0002 RETURN int(1)
优化后
php -d opcache.opt_debug_level=0x20000 test.php
0000 RETURN int(1)
此示例适用的其他方式也一样,通过摆脱不必要的JMP
/ JMPZ
/JMPNZ
这将在PHP可以以其它方式使用操作码if
块。
if (strlen('Test') < strlen('Test Test')) {
echo "Test";
}
0000 ECHO string("Test")
0001 RETURN int(1)
因为strlen('Test') < strlen('Test Test')
始终求值为true
,所以优化的OPCode不包含任何跳转。
完全合格的功能名称
关于特殊函数处理的一个警告是,当在名称空间内进行特殊函数调用时,引擎无法使用特殊处理,因为稍后可能在程序中声明具有相同名称的函数。
namespace Foo;
if (strlen('Test') < strlen('Test Test')) {
echo "Test";
}
请注意,if
块是如何在Foo
命名空间内的。OPCode转储显示PHP在此处未应用特殊处理:
php -d opcache.opt_debug_level=0x20000 test.php
0000 INIT_NS_FCALL_BY_NAME 1 string("Foo\strlen")
0001 SEND_VAL_EX string("Test") 1
0002 V1 = DO_FCALL_BY_NAME
0003 INIT_NS_FCALL_BY_NAME 1 string("Foo\strlen")
0004 SEND_VAL_EX string("Test Test") 1
0005 V2 = DO_FCALL_BY_NAME
0006 T0 = IS_SMALLER V1 V2
0007 JMPZ T0 0009
0008 ECHO string("Test")
0009 RETURN int(1)
大多数现代PHP代码库都使用名称空间,并且要带回特殊处理,可以在函数名称前添加反斜杠(\
)或在use function
子句中添加别名。它们通常被称为“标准功能名称”。
namespace Foo;
if (\strlen('Test') < \strlen('Test Test')) {
echo "Test";
}
或者
namespace Foo;
use strlen;
if (strlen('Test') < strlen('Test Test')) {
echo "Test";
}
上面的两个片段都向引擎发出信号,表明这些strlen
调用确实是针对内部strlen
函数的,这使引擎可以继续并内联它们。
借助FQFN,引擎可以再次发挥其魔力:
0000 ECHO string("Test")
0001 RETURN int(1)
本文的要旨是,在调用PHP内部函数(特别是具有内联功能的函数)时,请始终使用完全限定的函数名来对它们进行引擎优化。
具有特殊处理的功能列表
以下所有功能在Zend Engine中都有特殊处理,如果被称为“完全合格的功能名称”,则可以从中受益。
array_key_exists
array_slice
boolval
call_user_func
call_user_func_array
chr
count
defined
doubleval
floatval
func_get_args
func_num_args
get_called_class
get_class
gettype
in_array
intval
is_array
is_bool
is_double
is_float
is_int
is_integer
is_long
is_null
is_object
is_resource
is_scalar
is_string
ord
sizeof
strlen
strval
via https://php.watch/articles/php-zend-engine-special-inlined-functions