PHP:类名常量 vs 字符串性能

11

从PHP 5.5开始,有一个特性可以使用内置的魔术常量class来获取完整命名空间的类名。例如:

<?php
namespace Something\Obscenely\Long\Hard\To\Type;

class MyClass {
}

echo MyClass::class;
// Output: Something\Obscenely\Long\Hard\To\Type\MyClass
?>

以下是文档和RFC链接: http://php.net/oop5.basic#language.oop5.basic.class.class https://wiki.php.net/rfc/class_name_scalars 问题是:
如果我使用Zend Framework 2,例如,它被认为是一个具有巨大PHP数组配置的框架,如果我对每个类名在这些配置中使用解析方法::class而不是像字符串一样键入完整名称'',是否会有可观的性能影响?
例如:
'controllers' => [
        'invokables' => [
            '\Controller\Monitor'  => 'Import\Controller\MonitorController',
...

vs

'controllers' => [
            'invokables' => [
                '\Controller\Monitor'  => MonitorController::class,
    ...

更新:

我的个人测试

我编写了简单的快速测试来进行基准测试。

class MyClass
{
}

class MyClass1
{
}

class MyClass2
{
}

class MyClass3
{
}

/**
 * run many iteration loop for test percentage
 */
function testString()
{
    $results = [];

    for ($i = 0; $i < 150000; $i++) {

        $results[] = [
            'controllers' => [
                'invokables' => [
                    '\Controller\Monitor'  => 'Import\Controller\MonitorController',
                    '\Controller\Monitor2' => 'Import\Controller\MonitorController2',
                    '\Controller\Monitor3' => 'Import\Controller\MonitorController3',
                    '\Controller\Monitor4' => 'Import\Controller\MonitorController4',
                ]
            ]
        ];
    }

    return $results;
}

function testClass()
{
    $results = [];

    for ($i = 0; $i < 150000; $i++) {

        $results[] = [
            'controllers' => [
                'invokables' => [
                    '\Controller\Monitor'  => MyClass::class,
                    '\Controller\Monitor2' => MyClass1::class,
                    '\Controller\Monitor3' => MyClass2::class,
                    '\Controller\Monitor4' => MyClass3::class,
                ]
            ]
        ];
    }

    return $results;
}

$token = Benchmark::start('testString');

testString();

Benchmark::end($token);

$token = Benchmark::start('testClass');

testClass();

Benchmark::end($token);

exit();

结果类似于:
testString
215335.203125 Kbytes
Time: 0.2604 Seconds
testClass
215337.1640625 Kbytes
Time: 0.2508 Seconds

我们可以看到,::class 更快。在最新的php5.6下运行。
这是真的吗?
P.S. 这不是 How to measure PHP code 的重复问题:
1. 搜索引擎中没有我所问的信息。 2. 我不知道如何在这个问题上下文中测量性能,因为对于这种情况,仅运行基准测试是不够的并且困难的。 3. 在实际项目配置文件中更改成千上万行的代码不简单。 4. 我想要更详细的答案 - 不仅仅是数字,还要包括原因。

为什么不做一个简单的测试呢? - Robert
在真实项目配置文件中更改数千行代码以进行测试并不简单。 - Oleg Abrazhaev
1个回答

29
当您的代码中出现::class时,实际上发生的是PHP将您指定的类名转换为完全限定的命名空间类名并生成字符串。因此,如果您只提供了一个字符串,使用这种写法确实比直接提供字符串要多做更多的工作。
然而,这个过程是在编译时完成的。这意味着它只会执行一次,并且超出了程序运行时的范围。因此,您的性能计时器Benchmark不会考虑到这一点——实际上,当您的代码运行时,它只看到一个字符串,这意味着您实际上正在对两种情况下程序执行几乎完全相同的操作进行基准测试。两者之间的时间差异是一个人为的因素,可以忽略不计。
另一个关键点是,由于这个过程是在编译时完成的,如果启用了OpCache,则它只需要在第一次运行时转换字符串一次,这意味着两者之间的差异甚至更小。 ::class语法的添加并不是出于性能原因;它是为了帮助改善代码质量、可读性和可维护性。事实上,这些因素几乎总是比微观优化的性能更重要。
关于基准测试的进一步说明:
由于这是在编译时完成的,因此无法从PHP程序内部获得准确的基准测试结果。
如果您真的想要对其进行基准测试,最好的选择是编写一个shell脚本并使用计时器从那里调用程序。您将需要为每个变化创建一个单独的PHP程序。但即使如此,您得到的任何基准测试结果也会有些不真实,并且不太可能有任何有意义的用处。您会根据是否在单个程序实例中运行多个循环或在shell脚本中运行循环而获得不同的结果。如果启用了OpCache,则还会获得不同的结果。
但最重要的是,您真的不应该考虑是否要基准测试这个——它只是一种语法优化;它不影响性能。或者如果影响,那影响太微小,不值得考虑。

优化性能的秘诀是先找出代码中最大的瓶颈,然后首先处理它们。而不是处理最小的瓶颈。


如何正确地进行基准测试,有没有一些方法可以做到这一点? - Oleg Abrazhaev
这个答案很棒! - Oleg Abrazhaev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接