Like most languages, PHP allows developers to accomplish the same tasks using several different methods. I think most of us stick with whatever habits we fall into without really ever examining which methods might be the most efficent: Is there any performance difference between for
and while
? Is the ternary operator slower or faster than an if
statement?
Practically speaking, PHP isn’t likely to be the bottleneck in your applications–the database server and network latency are the obvious suspects (and if PHP is your bottleneck, the argument could be made that you ought to be using something faster than an interpreted scripting language.) However, sometimes every CPU cycle counts. And even when site performance isn’t on the line, pride still is. 😉 So is it worth adapting your style of coding? I do some benchmarking to find out…
The test environment
For these tests, I am using PHP 5.1.6 installed with Fedora Core 4 on a Pentium Xeon 2.8GHz with a gig of RAM. I decided to use the “Benchmark” PEAR package due to it’s simplicity. All I’m using it to do is call a function a specified number of times and then return the average (mean) execution time. I am also using PHP from the command line instead of via a webpage to eliminate that variable. Finally, I also temporarily disabled the PHP memory limit. Now, on with the show…
Checking for empty variables
One very common task is simply checking to see if a variable contains a value. I am aware of at least six different ways to do this in PHP: the empty()
and is_null()
functions, and using the ==
and ===
operators against empty strings and null values. To test each of these, I set up the following test functions:
function empty_test($val) { if( empty($val) ) $a=1; } function is_null_test($val) { if( is_null($val) ) $a=1; } function null_eq_test($val) { if( $val == null ) $a=1; } function null_id_test($val) { if( $val === null ) $a=1; } function str_eq_test($val) { if( $val=='' ) $a=1; } function str_id_test($val) { if( $val==='' ) $a=1; }
I was originally calling each function in sequence when executing the same script. However, I noticed that the execution order affected the results, with the first function called being the slowest. Realizing that this was probably caused by memory allocation overhead, I switched to testing only one function with each script execution. Each of the test functions was executed 50,000 times and the mean execution time is below (Benchmark outputs the values in seconds written in scientific notation. I’ve converted this to microseconds and rounded it to to make it easier to read):
empty($foo) 14.050765 is_null($foo) 14.323392 $foo == '' 14.108510 $foo === '' 13.534307 $foo == null 14.134374 $foo === null 13.921852
Clearly using the identical operator with an empty string is the fastest of the bunch (which kinda suprised me). However, “faster” is a relative term. Whether changing your coding practices to save half a microsecond is worth doing is a decision that I leave to the reader. Though, in certain circumstances, a 3.7% reduction in execution time might be worth it.
The ternary operator
While not everyone is, I’m a huge fan of the ternary operator. It allows me to condense an if
statement into a bit-sized morsel. But even more helpful, when scanning code I know that if I see a statement with an ternary operator that it’s simply assigning a variable based on a certain condition. If I come across an if
statement, I have to mentally evaulate it to see what that chunk of code is doing. It could be simply assigning a variable like the ternary operator, but it could also be doing something else. Reading thousands of lines a code a day, every little mental shortcut helps. But, this post is about benchmarking, so on with the testing…
My two test functions are functionally equivalent:
function if_stmt ($senator) { if( $senator == 'clue' ) $internet = 'network of pipes'; else $internet = 'series of tubes'; return $internet; } function tern_op ($senator) { $internet = ( $senator == 'clue' ) ? 'network of pipes' : 'series of tubes'; return $internet; }
The results:
if_stmt 15.153918 tern_op 15.307512
Ah hah! The sworn enemies of ternary operator seem to have a bit of evidence to back them up. Though, I’m not sure how much geek cred a 1% performance edge buys them.
Loopty loops
The first thing every PHP newbie learns is echo()
(or print()
–but we’ll get to that in a minute). One page two, we learn the three basic loops: for
, while
, and do
. I set up my test functions to give them each a whirl:
function test_for($foo) { for( $x=0; $x<=100; $x++ ) { $a=1; } } function test_while($foo) { while( $x<=100 ){ $a=1; $x++; } } function test_do($foo) { do { $a=1; $x++; } while ($x<=100); }
The results (again, the mean execution time amoungst 50,000 iterations):
for 31.192107 while 33.373895 do 32.129226
Oddly enough, for
is 3% faster than the next fastest statement, do
. I wish I understood why I am getting these results. It would be interesting to see if other developers (or other languages) report similiar findings. However, since I rarely use while
and never use do
, it looks like I'm actually doing something the most efficient way for a change.
The humble echo()
echo()
: Probably the most commonly used function (or "language construct" as the docs define it) in PHP. Surely if we could gain an performance advantage anywhere, it would most beneficial with echo()
. So let's try a few variations on echo()
and his brother print()
(what's the difference, you say?):
function test_echo1($foo) { echo $foo; } function test_echo2($foo) { echo($foo); } function test_print1($foo) { print $foo; } function test_print2($foo) { print($foo); }
For each of these functions, I'm just outputting one "a" per iteration. But I'm going to pipe the output to /dev/null
instead of the screen to avoid the interactive delay of 50,000 characters being sent via SSH:
echo 21.585264 echo() 21.558852 print 21.598616 print() 21.544270
Wow... I'm not even going to bother calculating the percentages. Echo or print, parenthesis or not, it doesn't seem to matter. Quite the disappointment.
Conclusion
I was hoping to find a nugget of wisdom after an evening of testing and writting, but unfortunately experiments don't always turn out the way you intend. But that's probably why they call them experiments. It's interesting that the ===
(indentical) operator is slightly faster than the more commonly used ==
(equals) operator. It's also worth noting that for
is around 3% faster than the other loop statements. However, since I don't understand why this is, I'm not wholly convinced that my methodology isn't flawed somehow. I would appreciate it if someone could double check my findings on this one.
When I'm feeling more inspired, I'll revisit PHP benchmarking to compare various network and file i/o operations, compression techniques, and using echo
to output HTML rather than embedding a series of PHP code blocks in a page.