Performance of static methods vs. functions
In PHP, (unlike what I originally thought) there is an overhead of calling static methods vs simple functions.
On a very simple bench, the overhead is over 30% of the calling time (the method just returns the parameter):
// 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";
outputs:
Static Time: 0.640204906464 ms
Object Time: 0.48961687088 ms
Function Time: 0.438289880753 ms
I know the actual time is still negligible unless I am actually calling something 1 million times, but the fact is that its there.
Will anyone care to try and explain what is happening behind the scenes?
update:
- added object method bench
Solution 1:
Apparently this point has been fixed in later versions of PHP (5.5.12).
I ran the OP's code (with empty methods), and I get these results :
Static Time: 1.0153820514679 ms
Object Time: 1.100515127182 ms
Edit: Eight months and some releases later...
It's interesting to see how Zend and the community are working hard on PHP's performance.
🐘 PHP 5.6
Here is the same benchmark with PHP 5.6.9
(ZE 2.6) :
Static Time: 0.97488021850586 ms
Object Time: 1.0362110137939 ms
Function Time: 0.96977496147156 ms
For one run, "object time" was even faster than static time, so now they are very close. Better, we can see that objects are almost fast as functions!
🐘 PHP 7.0
I've also compiled PHP 7.0 alpha 1
(ZE 3.0) and it is amazing to see how a fast language like PHP (Compared to other dynamic languages as you can see here or here) can be optimized again and again:
Static Time: 0.33447790145874 ms
Object Time: 0.30291485786438 ms
Function Time: 0.2329089641571 ms
With PHP7, basic functions have been greatly optimized, and "static time" is again slower than "instance/object time".
Edit, October 2015 one year later : PHP 7.0 RC5
. Now, "static time" is faster. An important thing to note: scalar type hinting (new feature in PHP7) brings a significant overhead, it's about 16% slower (type hinting does not make your code 16% slower, it's that slower when you code is only composed of function calls ;) In real life applications, this is negligible). Such an overhead could seem illogic, but it's less surprizing when you know that dynamic typing is at the core of PHP. Contrarily to other more-static languages, type hinting in PHP means more checks for the Zend Engine, and not less as some of us could expect. In the future, we will probably get more runtime optimizations on this point (exactly like HHVM's runtime code analyses and JiT approach). Do not forget that PHP7 is young, and all the cleanup that has been done for this release permits great enhancements in the future, in features and performance.
🐘 HHVM
A test against HHVM 3.7.1
still shows that HHVM easily wins on that type of benchmarks, here you can see the benefits of a JiT compilation (JiT is a "planned" feature for future versions of PHP, we'll probably get it in the 7.x or 8.x branches. Zend had created a PoC, as an OpCache extension):
Static Time: 0.070882797241211 ms
Object Time: 0.23940300941467 ms
Function Time: 0.06760311126709 ms
For HHVM, functions and static methods have a very similar timing, this could let us think that, internally, those are almost the same things (after all, a static method is very similar to a namespaced function). The instance timing is "catastrophic" compared to the others. This shows how HHVM and ZE are very different engines.
Conclusion?
There's no guarantee that one of these practices (static/instance) will stay the faster, forever. Use what seems the best in terms of software design and keep a coherent code into an existing application.
If you have the choice, and/or if you're writing a library, etc, then maybe you could use instance methods, it's more friendly with DI environments, and that gives more control to the developer that consumes your API.
If you're just providing utility functions (like those little packages in npm's ecosystem), then you could use namespaced functions (but be aware that PHP still doesn't have function autoloading, meaning that Composer can't lazy-load your library like it does with PSR-0/4)
Solution 2:
There used to be a big penalty when calling a static method - but it's fixed in 5.4.0 - see the extensive test results http://www.micro-optimization.com/global-function-vs-static-method .
Solution 3:
I repeated the test on my machine multiple times and surprisingly you are right!
In PHP calling methods of static
class seems to be slower than calling object methods. Click here for simple test.
The code with the running test is in the above link.
I even tried placing both the objet method and the static method in the same class and the static
method still results SLOWER!!!
At this point I'm wondering how slow could be a call to a static
method of an inherited class, since inheritance adds up delay.
Sadly, I'm clueless about the reason. Maybe PHP takes more time in finding the definition of the static
method.
As a side note I could only say that in a real life application it usually happens to have the object created before calling one of its methods. Therefor your test should take this into account comparing the loop of static calls to a loop that each time (or at least some times) [*] creates the objet:
for($i=0; $i<10*1000*1000; $i++)
{
$someObj = new someObj();
$someObj->doTest($i);
}
thus is obviously slower than the static
call.
for($i=0; $i<10*1000*1000; $i++)
{
SomeClass::doTest($i);
}
[*] the problem is: how much is some times in order to simulate what happnes in a real world app? It's hard to say!
Solution 4:
There is some thing Wrong in your tests. With a website designed to work with multiple users at the same time you have to create an object for each one. To run that object's method in your tests you should have:
for($i=0; $i<10*1000*1000; $i++)
{
$someObj = new someObj();
$someObj->doTest($i);
}
If your object had more properties and methods then creating it is slower and PHP uses more memory. A static method won't have this problem, and therefore using static methods is a better choice in lots of situations. For example, a class with some handy tools with static methods for common tasks.
Solution 5:
I have followed up and done the same test with tons of iterations on PHP 8.0.3.
Opcache doesn't make much of a difference in this test.
Without 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
With 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