PHP: Function Calls vs Object Method Calls vs Class Method Calls
Today I was looking at different ways to speed up some php scripts. Eventually I decided to go back and double check to see which was actually the fastest on the computer and php versions I was using.
I did the tests on a function, a static method, and two object methods, one with a parameter and one without, the results should show why. In each function, I went through an array and peformed a few functions on each. The array was randomly generated with another script. It has 129 elements. Each element has a key 32 bytes long and a value 4KB long. Finally, just to be fair, each test was run 5 times, only taking the fastest result.
I expected the function to outpeform the objects and classes, the static method to be in second, and for the second object to shine above the first one.
Here's the script I used to benchmark. You can use it to test for yourself or let me know of any errors in it, I know for a fact that I copied and pasted a bit too much at first (no, they shouldn't all take the exact number of ms).
// this is a very large array. // about 129 elements with 32 byte keys and 4KB value
echo 'The Function took: ' . number_format($time['function'] * 1000, 4) . 'ms<br> The Class took: ' . number_format($time['class'] * 1000, 4) . 'ms<br> The First Object took: ' . number_format($time['object'] * 1000, 4) . 'ms<br> The Second Object took: ' . number_format($time['object2'] * 1000, 4) . 'ms';
The machine tested on was a CentOS 4.3 with 512 MB RAM and a 733Mhz P3. When tested with a web server, Apache v2.2.2 was used with PHP setup with FastCGI and suexec.
With PHP 5.1.4, on console with no Accelerator, I tested 3 times because a few of the results varied.
The Function took: 1.8969ms
The Class took: 1.9600ms
The First Object took: 1.8070ms
The Second Object took: 1.6961ms
The Function took: 1.9751ms
The Class took: 1.7090ms
The First Object took: 1.7331ms
The Second Object took: 1.7149ms
The Function took: 1.6789ms
The Class took: 1.5011ms
The First Object took: 2.0032ms
The Second Object took: 1.6921ms
Going by these results, there is no clear winner. The function shows strength in the second test. Both Objects show their strenghts in the First test, and the second one shows its strength in the Third test. Finally, the class shows its strength in the second and third test.
Going by this, which one should you use? Does't appear to matter much anymore. They all go the same speeds. But being the person that I am, I decided to see how it would peform with an Accelerator.
I used eAccelerator v0.9.5-beta2 and ran the test 6 times and here are the final three results (the first three times for the accelerator/server to cache script):
The Function took: 1.5180ms
The Class took: 1.9290ms
The First Object took: 1.8249ms
The Second Object took: 1.4539ms
The Function took: 1.7889ms
The Class took: 1.5960ms
The First Object took: 1.8580ms
The Second Object took: 1.5540ms
The Function took: 1.7719ms
The Class took: 1.8320ms
The First Object took: 1.7920ms
The Second Object took: 1.5960ms
With an accelerator the results are more steady. The second object shines every time. The First object, function, and class both swap off places as in the other tests.
So now you know its possible to use OOP to go faster than you would with normal function calls. But if you think I stopped these tests here you were wrong.
Since PHP 5.2 RC1 is out, I decided to also test with that, especially since I've heard there have been many optimizations made in PHP 5.2. Here are the results from the console test (no accelerator):
The Function took: 1.7679ms
The Class took: 1.4899ms
The First Object took: 1.7741ms
The Second Object took: 1.6789ms
The Function took: 1.7662ms<br>
The Class took: 1.8530ms<br>
The First Object took: 1.8070ms
The Second Object took: 1.4200ms
The Function took: 1.7700ms<br>
The Class took: 1.6489ms<br>
The First Object took: 1.7800ms
The Second Object took: 1.6079ms
Similar to results with the accelerator on 5.1.4. The Second Object won every time... almost. The static class method won once, but that one time it went 0.3ms faster than everything else. The function kept a pretty consise speed, but it is definately not in the lead.
Results with PHP 5.2.0RC1 and eAccelerator v0.9.6-dev (SVN Revision 263).
The Function took: 1.7970ms
The Class took: 1.7869ms
The First Object took: 1.7481ms
The Second Object took: 1.6541ms
The Function took: 1.8730ms
The Class took: 1.6170ms
The First Object took: 1.8201ms
The Second Object took: 1.7350ms
The Function took: 1.6630ms
The Class took: 1.9059ms
The First Object took: 1.7660ms
The Second Object took: 1.3831ms
Similar results again.
Conclusion: Programming Correctly, OOP can outpeform normal function calling every time.
Some things to consider:
I did not include the creation of the objects in the time tested. Including it will most likely affect results, but there is a reason I didn't include it (explained below).
Now, the differences between using each of these techniques may be insignificant in many cases. 0.0004 seconds isn't much time to save and most people won't notice it or even care that you just saved .003 execution time in your scripts by switching to a more OOP approach. These benchmarks are probably more useful for recursive functions that have 500+ calls in your script. While .003 seconds time isn't much to save 1.5 might be a bit more to try to save.
The scope of your test is too small to make any such predilect conclusions. There are many factors you aren't considering here such as:
- execution stack depth (affects seek time in memory/secondary cache)
- object hashing (affects creation/storage/seek time)
You should also consider situations where OOP benefits are made clear, where workarounds in procedural programming (PP) require extensive mechanisms. For example, replicating an Interface mechanism in PP will by far tax your machine much more heavily than simply using the pre-baked PHP5 Interface classes. There's not much executional difference in PHP where memory pointers are concerned whether they are stored as $v or Object::$v - in PHP, a function pointer, is a function pointer...
Least to say, I appreciate your intent, but your test was too lightweight - PHP is also very susceptible to interpretation time, your tests were too short; and I'd add that every programming paradigm has its place.
Instead of looping arrays, do something constructive like an OOP-built A* search of a large search space, like solving a 15-puzzle or a Euler tour of a large network, and then reproduce the results using procedural programming. Run it 100 times subsequently, in 10 execution batches, and take the average result. This is the type of testing that's expected of any standard university project on the topic, and it should be adopted.
Good start, but you must look further. that .2 ms disparity could be your drive's cylinder seek time in the end.
Thanks for your input. I agree that my test is small, short, and has many other factors to consider. I do think this is a little more accurate than some benchmarks I found while searching the web for other's results. This is a very short test and probably very specific to my computer. I would expect that other computers, (especially servers) would have very different results.
Now I guess I should work on making a more complex script do another test on.