静态方法与function的性能
在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";
输出:
Static Time: 0.640204906464 ms Object Time: 0.48961687088 ms Function Time: 0.438289880753 ms
我知道实际时间是微不足道的,除非我实际上调用了一百万次,但实际上是在那里。
有人会试图解释幕后发生的事情吗?
更新:
– 增加了对象方法工作台
显然这一点已经在更高版本的PHP(5.5.12)中得到修复。
我运行OP的代码(用空方法),并得到这些结果:
Static Time: 1.0153820514679 ms Object Time: 1.100515127182 ms
编辑:八个月和一些发布后…
了解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
对于一次运行来说,“对象时间”比静态时间更快,所以现在它们非常接近。 更好的是,我们可以看到物体几乎是快速的function!
🐘PHP 7.0
我也编译了PHP 7.0 alpha 1
(ZE 3.0),很高兴看到像PHP这样的快速语言(与其他dynamic语言相比,您可以在这里或这里看到的)可以一次又一次地优化:
Static Time: 0.33447790145874 ms Object Time: 0.30291485786438 ms Function Time: 0.2329089641571 ms
使用PHP7,基本function得到了极大的优化,“静态时间”再次比“实例/对象时间”慢。
编辑,一年后的2015年10月 : PHP 7.0 RC5
。 现在,“静态时间”更快。 需要注意的一件重要的事情是:标量types提示(PHP7中的新特性)会带来很大的开销,速度会降低大约16%(types提示不会让代码慢16%,而当代码仅由函数调用组成时, )在现实生活中,这是微不足道的)。 这样的开销可能看起来不合逻辑,但是当你知道dynamictypes是PHP的核心时,它并不令人惊奇。 与其他更多静态语言相反,在PHP中键入提示意味着对Zend引擎的更多检查,而不是像我们中的一些人所期望的那样。 在将来,我们可能会得到更多的运行时优化(就像HHVM的运行时代码分析和JiT方法)。 不要忘了,PHP7是年轻的,所有已经完成的这个版本的清理允许未来在function和性能方面有很大的提升。
🐘HHVM
对HHVM 3.7.1
的testing仍然表明,HHVM很容易赢得这种types的基准testing,在这里你可以看到JiT编译的好处(JiT是未来PHP版本的“计划”function,我们可能会在7.x或8.x分支Zend创build了一个PoC,作为OpCache扩展 ):
Static Time: 0.070882797241211 ms Object Time: 0.23940300941467 ms Function Time: 0.06760311126709 ms
对于HHVM,函数和静态方法有非常相似的时间,这可以让我们认为,在内部,这几乎是相同的东西(毕竟,静态方法非常类似于名称空间函数)。 实例时机与其他时机相比是“灾难性的”。 这显示了HHVM和ZE是如何非常不同的引擎。
结论?
不能保证这些做法之一(静态/实例)将保持更快,永远。 使用软件devise方面看起来最好的方法,并在现有的应用程序中保持一致的代码。
如果你有select,和/或如果你正在编写一个库等,那么也许你可以使用实例方法,它是更友好的DI环境,这使得更多的控制消费者的API的开发。
如果你只是提供实用函数(就像npm生态系统中的那些小软件包),那么你可以使用命名空间函数(但是请注意, PHP仍然没有函数自动加载 ,这意味着Composer不能像你的库一样延迟加载它与PSR-0/4有关)
在调用一个静态方法时,曾经有一个很大的惩罚 – 但它在5.4.0中已经修复 – 请参阅广泛的testing结果http://www.micro-optimization.com/global-function-vs-static-method 。
我多次在我的机器上重复testing,令人惊讶的是你是对的!
在PHP调用static
类的方法似乎比调用对象方法慢。 点击这里进行简单的testing。
运行testing的代码在上面的链接。
我甚至尝试将objet方法和静态方法放在同一个类中, static
方法仍然导致SLOWER!
在这一点上,我想知道如何调用inheritance类的static
方法有多慢,因为inheritance增加了延迟。
可悲的是,我对这个理由毫无头绪。 也许PHP需要更多的时间来findstatic
方法的定义 。
作为一个侧面说明,我只能说在实际应用中,它通常会在调用其中一个方法之前创build对象。 因此,你的testing应该考虑到静态调用的循环,每次(或至less一些时候 )创buildobjet的循环:
for($i=0; $i<10*1000*1000; $i++) { $someObj = new someObj(); $someObj->doTest($i); }
因此明显比static
调用要慢。
for($i=0; $i<10*1000*1000; $i++) { SomeClass::doTest($i); }
问题是:有多less次是为了模拟真实世界中的应用程序? 很难说!
testing中有一些错误。 有一个网站devise与多个用户在同一时间工作,你必须为每一个创build一个对象。 要在testing中运行该对象的方法,您应该:
for($i=0; $i<10*1000*1000; $i++) { $someObj = new someObj(); $someObj->doTest($i); }
如果你的对象有更多的属性和方法,那么创build它会更慢,PHP使用更多的内存。 静态方法不会有这个问题,因此在很多情况下使用静态方法是一个更好的select。 例如,一个类与一些方便的工具与静态方法的常见任务。
自从我完成任何PHP以来,这已经有一段时间了,但这可能与您在其他编程环境中所期望的类似。
静态方法可能需要在每次被调用时在幕后构build一个SomeClass对象,而该函数可以在没有任何启动成本的情况下执行。 创build一个对象可能会花费很多,具体取决于许多事情:垃圾回收器/引用计数器破坏现有对象,导致碎片的内存压力,C运行时等内存不足的内存分配策略等。
比较现有对象的方法性能将会很有趣。 为此,创build一个SomeClass的实例,然后重复调用一个实例方法。
在静态方法的情况下,PHP必须检查方法是否可以从调用上下文(public,protected,private)中调用。 这很可能是导致开销或至less部分开销的原因,因为经典函数调用不需要PHP执行这种检查。