68

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

7
  • Is there any optimizer / byte code cache enabled in your test? Commented Sep 24, 2009 at 16:39
  • Try repeating the test with an optimization framework in place. See what you get then. Commented Sep 24, 2009 at 16:55
  • @Will Bickford - using eaccellerator on a different machine, $object->method() benches at about 95% the speed of static calls (or 105% bench time). Commented Sep 24, 2009 at 17:38
  • What version of PHP are you testing on? With 5.2.10, no accelerators and empty function bodies, I'm getting normalized mean times of 1.30, 1.16 and 1 for static, object and plain function calls after 10 trials. Commented Sep 25, 2009 at 3:33
  • 1
    I added a simple test in my answer so anyone can easily see the result and think about it: stackoverflow.com/questions/1472721/… Commented Aug 20, 2011 at 15:30

9 Answers 9

68

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)

Sign up to request clarification or add additional context in comments.

7 Comments

can you provide a 3v4l link to what you ran?
Yes please. What was the code? Was it the one from the question? Or was it something entirely different? We are measuring air molecules here. Full control of the measurement apparatus is everything that matters.
ey can you update your code with tests against PHP 7.0-release and 7.1 and 7.2 and 7.3 and 7.4? i like benchmarks!
Would you please run it again with PHP 7.1 , 7.2 , 7.3 and 7.4? I will be appreciate it
@Sz: The code was the same. Just that dfference between functions/instance/static methods was tested, nothing more, nothing less. Each time, I ran the program multiple times to ensure that I had stable results. The tests were run under Linux with the least concurrent programs as possible.
|
23

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 .

Comments

9

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!

Comments

6

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.

Comments

6

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 

Comments

3

It has been a while since I have done any PHP but this is probably similar to what you expect in other programming environments.

It is likely that the static method requires some construction of a SomeClass object behind the scenes each time that it is called, whereas the function can just be executed without any startup cost. Creating an object could be costly depending on a number of things: destruction of existing objects by a garbage collector/reference counter, memory pressure causing fragmentation, suboptimal memory allocation policies in the C runtime etc.

It would be interesting to compare the method performance of an existing object. To do this create an instance of SomeClass and then call an instance method repeatedly.

5 Comments

updated question with object method benchmark - not really the result i would imagine.
Why would calling a static method need to instantiate the object? The method is static, there is no object!
@Jesse the creation of an object can be implicit in the use of the static method. Since it is declared as part of the class, then the class to object instantiation is still involved in how the system executes the method.
@BrianLyttle Can you elaborate? I don't know specifically about the Zend Engine, but generally just being in the class doesn't mean an object needs to be instantiated. Static methods can be handled the same as normal global functions in that respect.
I know this is a little old, but this to me seems like the most likely reason. Static method calls still require the class to be built, because what if I call a private static method from a public static method? Objects themselves aren't built for static calls, but classes still need to be.
2

In the case of the static method, PHP has to check wether the method can or cannot be called from the calling context (public, protected, private). That's most likely what causes the overhead, or at least part of it, since the classic function call doesn't require PHP to perform that kind of check.

2 Comments

that makes sense - however, calling an object method is faster, and the same rules apply...
Maybe PHP checks wether a specific object method can be called from the current context or not, only once, and stores that information in memory as long as the execution loop remains in this same context...But does not do that for static methods. Man, you got me wondering why, now :) That a question you could ask on the PHP dev list !
2

I am following up what Morgan Touverey Quilling did but with PHP 7. Did 3 iterations incase it takes longer for the first run vs subsequent runs. Includes all classes as it might be done realistically. All included files just return the input.

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"; 

Just note that this is being done on one of my webhosts as its easier to switch php versions so might be some noise.

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 seemed to run a lot slower than the other versions on average. I found their lowest number but it got into the low .2####'s too. Don't' have 7.4 as of right now.

Comments

0

I've gotten curious by the results and ran it on PHP 8.4.11.

Using

function doTest() { $i = 5; $i *= 3; $i -= 2; } 

as the Test-Function.

Combined Times (10mio cycles each) are:

  • Static Time: 4.791208028793335 ms

  • Object Time: 6.265499114990234 ms

  • Function Time: 5.06414794921875 ms

Also I added a Logger-Phase for the default constructor of the hosting object. This one is only called in the object call variant.

Also Looking at the Opcode, it does not show the behaviour of creating objects for static function calls.

For the static function calls:

$starttime = microtime(true); for ($i = 0; $i<1000*1000*10; $i++) { SomeClass::doTest_static($i); } 
L0037 0010 INIT_FCALL 1 112 string("microtime") L0037 0011 SEND_VAL bool(true) 1 L0037 0012 V9 = DO_ICALL L0037 0013 ASSIGN CV2($starttime) V9 L0038 0014 EXT_STMT L0038 0015 ASSIGN CV3($i) int(0) L0038 0016 JMP 0022 L0039 0017 EXT_STMT L0039 0018 INIT_STATIC_METHOD_CALL 1 string("SomeClass") string("doTest_static") 

as for method calls:

$starttime = microtime(true); echo memory_get_usage() . PHP_EOL; for ($i = 0; $i<1000*1000*10; $i++) { $obj = new SomeClass(); $obj->doTest($i); } 
L0046 0049 INIT_FCALL 1 112 string("microtime") L0046 0050 SEND_VAL bool(true) 1 L0046 0051 V23 = DO_ICALL L0046 0052 ASSIGN CV2($starttime) V23 L0048 0053 EXT_STMT L0048 0054 INIT_FCALL 0 96 string("memory_get_usage") L0048 0055 V25 = DO_ICALL L0048 0056 T26 = CONCAT V25 string(" ") L0048 0057 ECHO T26 L0049 0058 EXT_STMT L0049 0059 ASSIGN CV3($i) int(0) L0049 0060 JMP 0070 L0050 0061 EXT_STMT L0050 0062 V28 = NEW 0 string("SomeClass") L0050 0063 DO_FCALL L0050 0064 ASSIGN CV4($obj) V28 L0051 0065 EXT_STMT L0051 0066 INIT_METHOD_CALL 1 CV4($obj) string("doTest") 

I can't tell above the examples above, but if it was different in past PHP versions, in 8.4.11 the static and object-methods behave very much as expected.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.