静态方法与函数的性能

Performance of static methods vs. functions

提问人:J.C. Inacio 提问时间:9/25/2009 最后编辑:AresJ.C. Inacio 更新时间:4/14/2021 访问量:33141

问:

在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

我知道实际时间仍然可以忽略不计,除非我真的调用了 100 万次,但事实是它就在那里。

有人愿意尝试解释幕后发生的事情吗?

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

PHP 性能 OOP

评论

0赞 Dennis C 9/25/2009
您的测试中是否启用了任何优化器/字节码缓存?
0赞 Will Bickford 9/25/2009
尝试在适当的优化框架下重复测试。看看你会得到什么。
0赞 J.C. Inacio 9/25/2009
@Will Bickford - 在另一台机器上使用加速器,$object->method() 工作台的速度约为静态调用的 95%(或 105% 的工作台时间)。
0赞 outis 9/25/2009
您正在测试哪个版本的 PHP?使用 5.2.10,没有加速器和空函数体,经过 10 次试验后,我得到了静态、对象和普通函数调用的标准化平均时间 1.30、1.16 和 1。
1赞 Marco Demaio 8/20/2011
我在答案中添加了一个简单的测试,这样任何人都可以轻松看到结果并思考它:stackoverflow.com/questions/1472721/......

答:

3赞 Brian Lyttle 9/25/2009 #1

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

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

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

评论

0赞 J.C. Inacio 9/25/2009
使用对象方法基准更新了问题 - 不是我想象的结果。
0赞 Jesse 2/22/2012
为什么调用静态方法需要实例化对象?方法是静态的,没有对象!
0赞 Brian Lyttle 2/24/2012
@Jesse对象的创建可以隐含在静态方法的使用中。由于它被声明为类的一部分,因此类到对象的实例化仍涉及系统如何执行该方法。
0赞 Jesse 2/26/2012
@BrianLyttle 你能详细说明一下吗?我没有具体了解 Zend Engine,但通常只是在类中并不意味着需要实例化对象。在这方面,静态方法的处理方式与普通全局函数相同。
1赞 Jeff Lambert 6/13/2012
我知道这有点老了,但对我来说,这似乎是最可能的原因。静态方法调用仍然需要构建类,因为如果我从公共静态方法调用私有静态方法会怎样?对象本身不是为静态调用而构建的,但类仍然需要为静态调用而构建。
2赞 Nicolas 9/25/2009 #2

对于静态方法,PHP 必须检查该方法可以或不能从调用上下文(public、protected、private)调用。这很可能是导致开销的原因,或者至少是部分开销,因为经典函数调用不需要 PHP 执行这种检查。

评论

1赞 J.C. Inacio 9/25/2009
这是有道理的 - 但是,调用对象方法更快,并且相同的规则适用......
0赞 Nicolas 9/25/2009
也许 PHP 检查是否可以从当前上下文调用特定的对象方法,只调用一次,并将该信息存储在内存中,只要执行循环保持在同一上下文中......但对于静态方法不这样做。伙计,你让我想知道为什么,现在:)您可以在PHP开发列表中提出这个问题!
9赞 Marco Demaio 8/13/2011 #3

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

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

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

我什至尝试将 objet 方法和静态方法放在同一个类中,但静态方法的结果仍然较慢!!

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

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

顺便说一句,我只能说,在现实生活中的应用程序中,它通常碰巧在调用其方法之一之前创建了对象。因此,您的测试应考虑到这一点,将静态调用的循环与每次(或至少某些次)[*]创建目标的循环进行比较:

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);
}

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

5赞 Jquest 9/5/2012 #4

你的测试中有一些错误。对于一个设计为同时与多个用户合作的网站,您必须为每个用户创建一个对象。若要在测试中运行该对象的方法,应具有:

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

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

23赞 Greg 12/8/2012 #5

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

67赞 Morgan Touverey Quilling 10/28/2014 #6

显然,这一点已在以后的 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

有一次,“物体时间”甚至比静态时间还要快,所以现在它们非常接近。更好的是,我们可以看到对象几乎像函数一样快!

🐘 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

在PHP7中,基本功能得到了极大的优化,“静态时间”再次比“实例/对象时间”慢。

编辑,2015 年 10 月一年后:PHP 7.0 RC5。现在,“静态时间”更快了。需要注意的重要一点是:标量类型提示(PHP7 中的新功能)带来了巨大的开销,它大约慢了 16%(类型提示不会使你的代码慢 16%,当你的代码只由函数调用组成时,它就会慢;)在实际应用中,这是可以忽略不计的)。这样的开销可能看起来不合逻辑,但当你知道动态类型是 PHP 的核心时,它就不那么令人惊讶了。与其他更静态的语言相反,PHP 中的类型提示意味着对 Zend Engine 的更多检查,而不是我们一些人所期望的更少。将来,我们可能会在这一点上获得更多的运行时优化(就像 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

对于 HHVM,函数和静态方法具有非常相似的时序,这可以让我们认为,在内部,它们几乎是相同的(毕竟,静态方法与命名空间函数非常相似)。与其他实例相比,实例时间是“灾难性的”。这表明 HHVM 和 ZE 是截然不同的引擎。

结论?

不能保证其中一种做法(静态/实例)会永远保持更快。在软件设计方面使用看起来最好的方法,并将连贯的代码保留到现有应用程序中。

如果你有选择,和/或者如果你正在编写一个库,那么也许你可以使用实例方法,它对 DI 环境更友好,这给了使用你的 API 的开发人员更多的控制权。

如果你只是提供实用函数(比如 npm 生态系统中的那些小包),那么你可以使用命名空间函数(但请注意,PHP 仍然没有函数自动加载,这意味着 Composer 不能像 PSR-0/4 那样延迟加载你的库

评论

3赞 a coder 3/25/2017
你能提供你运行的内容的 3v4l 链接吗?
0赞 Sz. 10/27/2018
是的,请。代码是什么?是问题中的那个吗?还是完全不同的东西?我们在这里测量空气分子。对测量设备的完全控制是最重要的。
0赞 hanshenrik 2/23/2019
你能用针对 PHP 7.0 版本、7.1 和 7.2 以及 7.3 和 7.4 的测试来更新你的代码吗?我喜欢基准测试!
0赞 GameO7er 12/2/2019
你能用PHP 7.1、7.2、7.3和7.4再次运行它吗?我将不胜感激
1赞 Morgan Touverey Quilling 5/11/2020
@Sz:代码是一样的。只是测试了函数/实例/静态方法之间的 dfferity,仅此而已。每次,我都会多次运行该程序,以确保获得稳定的结果。测试在 Linux 下运行,使用尽可能少的并发程序。
2赞 Adtopkek 6/7/2019 #7

我正在跟进 Morgan Touverey Quilling 所做的工作,但使用的是 PHP 7。是否进行了 3 次迭代,以防第一次运行与后续运行相比需要更长的时间。包括所有类,因为它可能实际完成。所有包含的文件都只返回输入。

include 'lib/global.php';
include 'SomeClass.php';
include 'StaticTest.php';

$someObj = new SomeClass();

$starttime = microtime(true);
for ($i = 0; $i< 10*100000; $i++)
    StaticTest::doStaticTest($i);

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

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

for ($i = 0; $i< 10*100000; $i++)
    $someObj->doObjTest($i);

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

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

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

echo "<br>Function Time: " , (microtime(true)-$starttime) , " ms\n";

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

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

for ($i = 0; $i< 10*100000; $i++)
    $someObj->doObjTest($i);

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

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

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

echo "<br>Function Time: " , (microtime(true)-$starttime) , " ms\n";

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

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

for ($i = 0; $i< 10*100000; $i++)
    $someObj->doObjTest($i);

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

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

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

echo "<br>Function Time: " , (microtime(true)-$starttime) , " ms\n";

请注意,这是在我的一个虚拟主机上完成的,因为它更容易切换 php 版本,所以可能会有一些噪音。

PHP 7.0.33

Static Time:   0.14076709747314 ms
Object Time:   0.16203689575195 ms
Function Time: 0.13194108009338 ms
Static Time:   0.13194918632507 ms
Object Time:   0.1779100894928 ms
Function Time: 0.13044309616089 ms
Static Time:   0.13045001029968 ms
Object Time:   0.16074585914612 ms
Function Time: 0.13029479980469 ms 

PHP的 7.1.29

Static Time:   0.13407206535339 ms
Object Time:   0.13267111778259 ms
Function Time: 0.1302649974823 ms
Static Time:   0.13027906417847 ms
Object Time:   0.1390438079834 ms
Function Time: 0.16873598098755 ms
Static Time:   0.16874289512634 ms
Object Time:   0.13901305198669 ms
Function Time: 0.12576103210449 ms 

PHP 7.2.18 版本:

Static Time:   0.1657600402832 ms
Object Time:   0.15700101852417 ms
Function Time: 0.1484169960022 ms
Static Time:   0.14842295646667 ms
Object Time:   0.16168689727783 ms
Function Time: 0.17508292198181 ms
Static Time:   0.17508983612061 ms
Object Time:   0.19771790504456 ms
Function Time: 0.1468551158905 ms 

PHP的 7.3.5

Static Time:   0.10701704025269 ms
Object Time:   0.097011089324951 ms
Function Time: 0.075740098953247 ms
Static Time:   0.07575798034668 ms
Object Time:   0.083790063858032 ms
Function Time: 0.072473049163818 ms
Static Time:   0.072479009628296 ms
Object Time:   0.081503868103027 ms
Function Time: 0.071882963180542 ms 

PHP 7.2 的平均运行速度似乎比其他版本慢得多。我找到了他们的最低数字,但它也进入了低 .2####。截至目前,不要有 7.4。

6赞 iio7 4/14/2021 #8

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

Opcache 在此测试中没有太大区别。

不使用 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

使用 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