静态方法与功能的表现

J.C*_*cio 61 php oop performance

在PHP中,(与我原先的想法不同),调用静态方法与简单函数的开销很大.

在一个非常简单的工作台上,开销超过调用时间的30%(该方法只返回参数):

// bench static method
$starttime = microtime(true);
for ($i = 0; $i< 10*1000*1000; $i++)
    SomeClass::doTest($i);

echo "Static Time:   " , (microtime(true)-$starttime) , " ms\n";

// bench object method
$starttime = microtime(true);

for ($i = 0; $i< 10*1000*1000; $i++)
    $someObj->doTest($i);

echo "Object Time:   " , (microtime(true)-$starttime) , " ms\n";

// bench function
$starttime = microtime(true);

for ($i = 0; $i< 10*1000*1000; $i++)
    something_doTest($i);

echo "Function Time: " , (microtime(true)-$starttime) , " ms\n";
Run Code Online (Sandbox Code Playgroud)

输出:

Static Time:   0.640204906464 ms
Object Time:   0.48961687088 ms
Function Time: 0.438289880753 ms
Run Code Online (Sandbox Code Playgroud)

我知道实际的时间仍然可以忽略不计,除非我实际上已经召唤了100万次,但事实是它在那里.

有人会关心尝试解释幕后发生的事情吗?

更新:
- 添加对象方法工作台

Mor*_*ing 54

显然,这一点已在PHP(5.5.12)的更高版本中得到修复.

我运行了OP的代码(使用空方法),我得到了这些结果:

Static Time:   1.0153820514679 ms
Object Time:   1.100515127182 ms
Run Code Online (Sandbox Code Playgroud)

编辑:八个月后发布一些版本......

有趣的是看看Zend和社区如何努力学习PHP的性能.

PHP 5.6

这与PHP 5.6.9(ZE 2.6)的基准相同:

Static Time:   0.97488021850586 ms
Object Time:   1.0362110137939 ms
Function Time: 0.96977496147156 ms
Run Code Online (Sandbox Code Playgroud)

对于一次运行,"对象时间"甚至比静态时间更快,所以现在它们非常接近.更好的是,我们可以看到对象几乎像函数一样快!

PHP 7.0

我还编译了PHP 7.0 alpha 1(ZE 3.0),看到像PHP这样的快速语言(与你在这里这里可以看到的其他动态语言相比)如何一次又一次地优化,真是太棒了:

Static Time:   0.33447790145874 ms
Object Time:   0.30291485786438 ms
Function Time: 0.2329089641571 ms
Run Code Online (Sandbox Code Playgroud)

使用PHP7,基本功能已经大大优化,"静态时间"再次慢于"实例/对象时间".

编辑,201510月一年后:PHP 7.0 RC5.现在,"静态时间"更快.要注意的重要一点:标量类型提示(在PHP7新功能)带来了显著的开销,这是慢约16%(类型提示并没有使你的代码慢16%,那就是当你的代码仅由函数慢通话; )在现实生活中,这可以忽略不计).这样的开销可能看起来不合逻辑,但是当你知道动态类型是PHP的核心时,它就不那么令人惊讶了.相反其他更静态的语言,PHP中的类型提示意味着更多的Zend引擎检查,而不是一些我们可以期待.将来,我们可能会在这一点上获得更多的运行时优化(就像HHVM的运行时代码分析和JiT方法一样).不要忘记PHP7是年轻的,并且为此版本所做的所有清理工作都将在未来,功能和性能方面实现重大改进.

HHVM

针对HHVM 3.7.1静态的测试表明HHVM很容易在这种类型的基准测试中获胜,在这里您可以看到JiT编译的好处(JiT是未来PHP版本的"计划"功能,我们可能会在7.x中获得它或8.x分支.Zend创建了一个PoC,作为OpCache扩展):

Static Time:   0.070882797241211 ms
Object Time:   0.23940300941467 ms
Function Time: 0.06760311126709 ms
Run Code Online (Sandbox Code Playgroud)

对于HHVM,函数和静态方法具有非常相似的时序,这可以让我们认为,在内部,这几乎是相同的事情(毕竟,静态方法与命名空间函数非常相似).与其他实例相比,实例时间是"灾难性的".这显示了HHVM和ZE是如何非常不同的引擎.

结论?

无法保证其中一种做法(静态/实例)将永远保持更快.使用在软件设计方面看起来最好的东西,并将一致的代码保存到现有的应用程序中.

如果您有选择,和/或如果您正在编写库等,那么也许您可以使用实例方法,它对DI环境更友好,并且可以为使用API​​的开发人员提供更多控制.

如果你只是提供实用的功能(如在故宫的生态系统中那些小包装),那么你可以使用命名空间功能(但要注意,PHP仍然没有功能自动加载,这意味着作曲家不能懒到您的书架一样它用PSR-0/4)

  • 你能提供3v4l的链接吗? (3认同)

小智 23

在调用静态方法时曾经有过一个很大的惩罚 - 但它已在5.4.0中修复 - 请参阅广泛的测试结果http://www.micro-optimization.com/global-function-vs-static-method.


Mar*_*aio 7

我多次在我的机器上重复测试,令人惊讶的是你是对的!

在PHP中,static类的调用方法似乎比调用对象方法慢.点击这里进行简单测试.

运行测试的代码在上面的链接中.

我甚至尝试将objet方法和静态方法放在同一个类中,该static方法仍然会导致SLOWER!

在这一点上,我想知道static对继承类的方法的调用有多慢,因为继承会增加延迟.

可悲的是,我对这个原因一无所知.也许PHP需要更多时间来找到static方法的定义.

作为旁注,我只能说在实际应用程序中,通常会在调用其中一个方法之前创建对象.因此,您的测试应考虑到这一点,将静态调用循环与每次(或至少某些时间)[*]创建objet 的循环进行比较:

for($i=0; $i<10*1000*1000; $i++)
{ 
   $someObj = new someObj();
   $someObj->doTest($i); 
}
Run Code Online (Sandbox Code Playgroud)

因此显然比static通话慢.

for($i=0; $i<10*1000*1000; $i++)
{ 
   SomeClass::doTest($i);
}
Run Code Online (Sandbox Code Playgroud)

[*]问题是:为了模拟真实世界应用程序中的快乐,有时会花多少时间?很难说!


小智 6

你的测试中有一些错误的东西.通过设计与多个用户同时工作的网站,您必须为每个用户创建一个对象.要在测试中运行该对象的方法,您应该:

for($i=0; $i<10*1000*1000; $i++)
{ 
   $someObj = new someObj();
   $someObj->doTest($i); 
}
Run Code Online (Sandbox Code Playgroud)

如果你的对象有更多的属性和方法,那么创建它会更慢,PHP会占用更多的内存.静态方法不会出现这个问题,因此在很多情况下使用静态方法是更好的选择.例如,一个带有一些方便工具的类,其中包含用于常见任务的静态方法.


iio*_*io7 6

我在 PHP 8.0.3 上进行了大量迭代并进行了相同的测试。

Opcache 在此测试中没有产生太大差异。

没有操作缓存:

Function Time:  0.15400409698486 ms
Static Time:    0.15216994285583 ms
Object Time:    0.19552803039551 ms
Function Time:  0.1428279876709 ms
Static Time:    0.15206789970398 ms
Object Time:    0.22962498664856 ms
Function Time:  0.14341592788696 ms
Static Time:    0.15271997451782 ms
Object Time:    0.22965002059937 ms
Function Time:  0.1877110004425 ms
Static Time:    0.1523380279541 ms
Object Time:    0.2297830581665 ms
Function Time:  0.14280891418457 ms
Static Time:    0.15206098556519 ms
Object Time:    0.22957897186279 ms
Function Time:  0.14343619346619 ms
Static Time:    0.15272903442383 ms
Object Time:    0.22955703735352 ms
Function Time:  0.14328694343567 ms
Static Time:    0.15257477760315 ms
Object Time:    0.22901511192322 ms
Function Time:  0.14302086830139 ms
Static Time:    0.15233588218689 ms
Object Time:    0.22931504249573 ms
Function Time:  0.14283490180969 ms
Static Time:    0.15209102630615 ms
Object Time:    0.22963285446167 ms
Function Time:  0.14345097541809 ms
Static Time:    0.1527111530304 ms
Object Time:    0.22959303855896 ms
Run Code Online (Sandbox Code Playgroud)

使用 opcache:

Function Time:  0.15897798538208 ms
Static Time:    0.15508103370667 ms
Object Time:    0.20733213424683 ms
Function Time:  0.14364719390869 ms
Static Time:    0.15376496315002 ms
Object Time:    0.18648386001587 ms
Function Time:  0.142982006073 ms
Static Time:    0.15293192863464 ms
Object Time:    0.20651602745056 ms
Function Time:  0.14292907714844 ms
Static Time:    0.15280795097351 ms
Object Time:    0.18663787841797 ms
Function Time:  0.14208316802979 ms
Static Time:    0.15290093421936 ms
Object Time:    0.20616102218628 ms
Function Time:  0.14288401603699 ms
Static Time:    0.15276694297791 ms
Object Time:    0.1861629486084 ms
Function Time:  0.14292597770691 ms
Static Time:    0.15292882919312 ms
Object Time:    0.20615196228027 ms
Function Time:  0.14286112785339 ms
Static Time:    0.1527988910675 ms
Object Time:    0.18700098991394 ms
Function Time:  0.14315795898438 ms
Static Time:    0.15318417549133 ms
Object Time:    0.20666813850403 ms
Function Time:  0.14300584793091 ms
Static Time:    0.15291309356689 ms
Object Time:    0.18714189529419 ms
Run Code Online (Sandbox Code Playgroud)


Bri*_*tle 3

我已经有一段时间没有做过任何 PHP 了,但这可能与您在其他编程环境中所期望的类似。

静态方法很可能每次调用时都需要在幕后构建 SomeClass 对象,而该函数只需执行即可,无需任何启动成本。创建对象的成本可能会很高,具体取决于许多因素:垃圾收集器/引用计数器破坏现有对象、导致碎片的内存压力、C 运行时中的次优内存分配策略等。

比较现有对象的方法性能会很有趣。为此,创建 SomeClass 的实例,然后重复调用实例方法。