Increasing PHP performance

This is just a short post on a couple of performance increases you can do in your code that was kindly pointed out to me by WhiteAcid, when he looked through the source code of my captcha. I must admit I wasn’t aware of these slight improvements and I’m sure this post will inform a few people of the potential performance gains. I’ve haven’t done any benchmarking on the following examples so if anyone knows of a good PHP benchmarking site, please send it in and I shall include the link here.

Conditions

The triple equals sign increases performance because PHP performs a strict check on the two variables.

<?php
if($variable1 === $variable2) {
//code
}
?>

Concatenation

I usually concat a string with the following:-

<?php
echo '';
?>

But a better way is to use commas to output the string because PHP only has to output it instead of using concatenation.

<?php
echo '';
?>

Benchmarks

I found this excellent site with some benchmark tests:-
PHP benchmarks

Also this on string benchmarking:-
String benchmarks

For vs while
Loops

Yet another benchmark, this one is quite comprehensive:-
Speed freaks

That’s all for now, I might include some more examples at a later time.

Timing class by Richard Heyes

Timing class

Have you got any performance tips you’d like to share? Leave a comment and I shall get in touch and choose the best ones which will be added to this post.

37 Responses to “Increasing PHP performance”

  1. Hugues writes:

    The second point is a common subject of controversy.
    Some benchmarks show the opposite. Facing comma separated arguments, PHP issues as manu ‘echo’ as there are arguments (at least in some 4.* versions).

    Example :

    echo ‘foo’, ‘fee’, ‘fum’;
    

    Is the same as

    echo ‘foo’;
    echo ‘fee’;
    echo ‘fum’;
    

    This can lead to a slower execution speed compared to the classic concatenated one.

    According to some PHP experts, the real fastest way for concatenated input is to use the implode() native PHP function.

    Example :

    echo implode(‘’, array(‘foo’, ‘fee’, ‘fum’);
    

    But seriously, the speed gain is so tiny regarding other potential application bottlenecks than it is better to opt for the most modular approach. And the comma separated output is quite inflexible.

    For example, with coma separated arguments, you can not switch from an ‘echo’ statement to a variable assignation.

    Example

    echo ‘foo’ . ‘fee’ . ‘fum’ ;
    
    $a =  ‘foo’ . ‘fee’ . ‘fum’ ;
    

    Believe me, it’s not a minor point. I still remember that I had to refactor a big application for a better separation between process and display. The previous code was full of coma separated echo statements. It took weeks to get rid of them.

    Hugues

  2. Gareth Heyes writes:

    Very good points Hugues, I think the decision is with the programmer to choose which option is best depending on the task.

  3. loginsuck writes:

    Why not just do some actual tests about your intutions on the command-line?

    $ time -f \%es php -r “\$var1 = ‘foo’; \$var2 = ‘bar’; for(\$i=0; \$i /dev/null
    1.58s
    $ time -f \%es php -r “\$var1 = ‘foo’; \$var2 = ‘bar’; for(\$i=0; \$i /dev/null
    1.08s

    Clearly, strict equals wins, as one would expect.

    However,

    $ time -f \%es php -r “\$var = ‘foo’; for(\$i=0; \$i /dev/null
    1.88s
    $ time -f \%es php -r “\$var = ‘foo’; for(\$i=0; \$i /dev/null
    1.00s

    Hugues is right, concatentation wins, with a greater improvement than the strict equals.

    Unfortunately, implode() is really slow, counter to his advice.

    $ time -f \%es php -r “\$var = ‘foo’; for(\$i=0; \$i /dev/null
    2.21s

  4. dancaragea writes:

    1. Use single quotes instead of double quotes for strings wherever possible. This saves the parser the overhead of searching for variables inside double quoted text.

    2. Use ++$i instead of $i++. The second one uses a temporary copy of $i to add 1 to, then assign to $i.

    3.

    for ($i=0;$i
    

    is slow

    $temp=count($some_array);
    for ($i=0;$i< $temp;++$i) {
    }
    

    is faster

    for ($i=0;isset($some_array[$i]);++$i) {
    }
    

    is fastest

    4. echo is faster than print because it is a language construct, not a function.

    5. use include '/some/file.php' instead of include('/some/file.php'). Same for require and the _once equivalents.

    6. Type cast instead of type convert (e.g use (int)$string instead of intval($string))

    7. In case of mysql functions use either mysql_fetch_assoc() or mysql_fetch_row() but not mysql_fetch_array() because [probably] you won't need both an index based array and an associative one too. Usually you will use either $array[$i] or $array['key'].

  5. Gareth Heyes writes:

    dancaragea excellent tips! Thanks very much for sharing them

  6. Robin Mehner writes:

    dancaragea:

    the for-loop that you consider as the fastest has one major drawback: it only works, if the array is continously numerated. You’ll run into problems, if there is a gap.

    $array    = array(1,2,3,4,5,6);
    unset($array[4]);
    
    for ($i=0;isset($array[$i]);++$i) {
        echo $array[$i], "\n";
    }
    

    will only output the array contents to “4”.

    that was the only thing i wanted to say to this 😉

  7. Gareth Heyes writes:

    We now have syntax colouring 🙂

    I hope you don’t mind but I edited some of your comments to help format and add syntax colouring. Thanks to everyone for the excellent feedback!

  8. Gareth Heyes writes:

    Robin I don’t think you’re correct because the isset function would be using the index not the value to see if the next array item exists. So it doesn’t matter if a value is removed.

    Unless I’m misunderstanding your point?

  9. Robin Mehner writes:

    Hi,

    I’m right, test the code and you’ll see what I mean. You want see the “6” in the output, because if $i get’s the value “4” and so isset checks isset($array[4]) and returns false and the loop breaks.

    $i does not know magically the index of the next item, it just gets increased.

    So the point is: If you haven’t a continously numerated array and want to get the for-loop into all contents of it, you can’t use the solution above.

    Hmm, or are we misunderstanding us each other? Don’t know, because of the tropical heat here I haven’t found much sleep the last days 😉

  10. Gareth Heyes writes:

    I Checked the code and yes I see what you mean, because isset is used it doesn’t look at the index of the array however if array key exist is used it will output the data out correctly e.g.

    < ?php
    $array    = array(1,2,3,4,5,6);
    for ($i=0;array_key_exists($i, $array);++$i) {
        echo $array[$i], "\n";
    }
    ?>
    

    I don’t know about the speed of this though, I think it would be slower so defeats the object really.

  11. Robin Mehner writes:

    Hi,

    array_key_exists has the same drawback, it breaks the loop if there is a gap (because the condition get’s false) … it’s only an other function call.

    so, “problem” (if the condition is that you want to have all contents of the array) is not solved.

    btw: is there a way to know if my comment is submitted correctly?

  12. Robin Mehner writes:

    argh, my code was sucked up by the system! (i’ve used the code format option mentioned above) … so the code was:

    $array  = array(1,2,3,4,5,6);
    unset($array[4]);
    
    for ($i = 0; array_key_exists($i, $array); ++$i) {
        echo $array[$i], "\n";
    }
    

    /*
    Output wanted:
    1
    2
    3
    4
    6

    Output you’ll get:
    1
    2
    3
    4
    */

  13. Gareth Heyes writes:

    Robin it doesn’t break the loop, I’ve tested it. See for yourself.

    Comments are in moderation before they are displayed to stop manual comment spam. At the moment the site doesn’t notify you if your comment is successful.

    Automated comment spam is protected against using my plugin SpamBam and we don’t get any automated spam. But there is no way to prevent manual comment spam submissions.

  14. Robin Mehner writes:

    There is no break in your code because you have no “unset” … so you have a continously numerated array 😉

    The condition is true for every check = it does not break in the middle of the array.

    Another thing that comes to my mind:

    is === really fast as ==? Because it’s one check more, which must be slower in my opinion. But I’m not sure for this one.

  15. Gareth Heyes writes:

    Sorry yes you’re correct, just retested. Sorry for the confusion but it is monday 🙂

  16. Gareth Heyes writes:

    This does work as expected:-

    < ?php
    $array  = array(1,2,3,4,5,6);
    $array[4] = null;
     
    for ($i = 0; array_key_exists($i, $array); ++$i) {
        echo $array[$i], "\n";
    }
    ?>
    
  17. Robin Mehner writes:

    hmm, I have a typo in my comment, my other question is: “Is === really fastER than ==, because it does one more check which naturally means it’s slower.”

    Again, not sure for this.

    And: Is there a way to edit comments? I’m at work and it’s monday … that’s the ideal situation for making many typos 😉

  18. Gareth Heyes writes:

    I might do a comprehensive benchmark test on the various functions etc but I was hoping that someone knew a site that had already done one so I can link to it in this post.

  19. Gareth Heyes writes:

    I’ve added a benchmark link to the post now.

  20. Gareth Heyes writes:

    Robin you should have an option to edit posts when you login, if not I shall have a look at the wordpress configuration.

  21. Robin Mehner writes:

    Thank you for the benchmark link, I will digg deeper into this == vs. === thing.

    And no, I have no option to edit a post or I am blind. I’m already logged in, in fact I must, because you aren’t allowed to write anonymous comments here 😉

  22. Gareth Heyes writes:

    If you find out more information I would be grateful if you can keep me informed, I could add it to the post and credit you with a site link.

    I shall look into the configuration because you should be able to edit your comments.

  23. Gareth Heyes writes:

    I’ve just added a few more benchmark links which I hope everyone finds useful.

  24. dancaragea writes:

    @Robin: you’re right, the code won’t work with any array but so could be the case with the other 2 (slower) methods. Since you’re testing just for $i<count($array), there’s no guarantee that $array[$i] exists.
    But in the situations where $array has consecutive values and no gaps the 3rd method is the fastest.

    @Gareth: in my first post there are some semicolons after < comparisions – they should be removed.

    $array[4]=null is not the same with unset($array[4]);

    I don’t think your array_key_exists() method would work in that case.
    Try your code with
    $array = array(1=>1,2=>2,3=>3,5=>5,6=>6);

    The better comparision would be for ($i=0;$i<count($array) && isset($array[$i]);++$i)
    Of course count() should be pre-read first.

    But the problem with this method is that if there are big gaps in the array it will loop through them for no reason. In this case you should use foreach()

  25. Gareth Heyes writes:

    Updated your comment with the corrected code. I don’t know why wordpress doesn’t allow people to edit their own comments. I hope to sort this out soon.

  26. Logan Buesching writes:

    What are your thoughts on premature optimization? IMO, most of these comments that have been posted *may* give you a few microseconds on each call, at most a few milliseconds. I would assume that if you spent the same amount of time attempting to optimize your slowest SQL call, you will get orders of magnitude better returns than worrying about whether or not you are concatinating or using a comma in your echo statements.

  27. Gareth Heyes writes:

    Yes obviously optimising your SQL statements would but this page was simply to point out ways to increase performance however slight.

    If you would care to share some tips then please do. I might add SQL tips at a later stage.

  28. Mgccl writes:

    dancaragea, about that

    for ($i=0;isset($some_array[$i]);++$i){	
    }
    

    I found it is not as fast as

      $temp=count($some_array);
    for ($i=0;$i
    

    My test are from this code, on PHP5.2:

    $some_array = range(0,100000);
    
        $timeparts = explode(' ',microtime());
      $starttime = $timeparts[1].substr($timeparts[0],1);
    for ($i=0;isset($some_array[$i]);++$i){
    	
    	
    }
      $timeparts = explode(' ',microtime());
      $endtime = $timeparts[1].substr($timeparts[0],1);
      echo bcsub($endtime,$starttime,6),'';
      
      $timeparts = explode(' ',microtime());
      $starttime = $timeparts[1].substr($timeparts[0],1);
      $temp=count($some_array);
    for ($i=0;$i';
    

    Also, in some cases, the array can be looped backwards. like this

    for ($i=count($some_array);$i;){
    --$i;
    }
    

    which is the fastest because compare to loop forward, one less conditional saved from every iteration.

  29. Robin Mehner writes:

    Hi,

    it’s me again. Don’t had the time for know to test the difference between “==” and “===” but for now I believe the benchmark (even if really GOOD benchmarks are seldom, because most of them missing important points)

    @Logan:

    Yes, that’s clear. I think this post is intended to show everyone some small optimizations in the script language itselfs, nothing more.

    It should be clear to everyone, that the real bottlenecks are elsewhere, so you can keep some simple rules:

    1. use external resources wisely
    2. do not code / use bloated code. Small code = fast code (in most cases ;))
    3. include only the files that you really need
    4. C code is faster than PHP code, so if you can solve the problem with an internal PHP function, do it!

    and the most important rule:

    5. Cache as much as possible!

    alone the fifth point make way more sense than most of the code optimizations you can do … and it keeps your code read able.

    SQL optimization is a good point to. If you work with MySQL I recommend you the book: “High performance MySQL” from Jeremy D. Zawodny

  30. Robin Mehner writes:

    Oh, and one other thing:

    Try Memcache for caching and xdebug for profiling, they both do their job very well.

    (I’m sorry for the typos in my previous post … switching between languages in the early morning ;-))

  31. molotov writes:

    @Mgccl:

    for ($i=count($some_array);$i;){
    –$i;
    }

    Is that the best way to this? Wouldn’t it be better to use a temporary variable instead of asking the for loop to count the array each time? It was mentioned earlier in the comments, but not-seeing it here makes me wonder if it was intentional, or just intending to show the decrement.

    $temp = count($some_array);
    for ($i=count($temp);$i;){
    –$i;
    }

  32. molotov writes:

    Uh, when copy/pasting the above, I forgot to remove the count() from the for loop, DUH.

    I’m not posting on a Monday morning; I’m posting at 12:30 on a Saturday night and blame the goof on the fact that I’m looking up PHP optimizations on a Saturday night 😛

  33. nickf writes:

    I’m no expert on this, but here’s my understanding:

    Regarding == vs ===, the identity check (===) actually will be doing less processing because it doesn’t have to cast variables to different types.

    I did some benchmarking:

    5,000,000 iterations
    TESTS:
    1.1: 1 == 1
    1.2: 1 == ‘1’
    1.3: 1 == 2
    1.4: 1 == ‘a’
    2.1: 1 === 1
    2.2: 1 === ‘1’
    2.3: 1 === 2
    2.4: 1 === ‘a’

    RESULTS:
    1.1: 4.2496 (108.0435%)
    1.2: 5.4232 (137.8819%)
    1.3: 4.8647 (123.6827%)
    1.4: 5.3762 (136.6878%)
    2.1: 4.0064 (101.8609%)
    2.2: 3.9332 (100%)
    2.3: 3.9355 (100.059%)
    2.4: 3.9373 (100.1045%)

    so you can see that identity checking is much quicker.

  34. Joseph M writes:

    I recently was having a performance problem iterating through a class in php 5 using $this to access a variable.

    I tried creating a reference to the variable ie
    $var = & $this->var;

    The speed tests were impressive.
    At 1,000,000 iterations
    This()
    Start Time:1188316233.75
    End Time:1188316234.3781
    Total Time:0.62805891036987

    Ref()
    Start Time:1188316234.3781
    End Time:1188316234.5309
    Total Time:0.15274381637573

  35. kvz writes:

    Another article about optimizing the performance of apache & php for high traffic loads:
    http://kevin.vanzonneveld.net/techblog/article/survive_heavy_traffic_with_your_webserver

  36. Andrew writes:

    A couple other performance saving tips:

    Use single quotes instead of double quotes if you have no variables in the string. When single quotes are used, PHP doesn’t bother to search the string for any variables.

    require_once() is more expensive than require().

  37. Peter writes:

    Use double quote string is best practice in PHP: http://www.linuxask.com/questions/should-i-always-use-single-quotes-for-php-strings