Friday, September 23, 2011

Optimizing PHP Through Habits

Hi Googler,

What has been a long interest of mine in writing simple, maintainable and secure (a.k.a. Good[tm]) code, has forked off the offspring of optimization.
There are nummerous discussions in the blogosphere about whether to use echo versus print, iffor() is faster than while(), etc. and though the gains are usually very small, I desided to add my thoughts to the debate.
I found an article on optimization through coding habits in Ilia Alshanetsky's zend performance slides and decided to test some of the claims. My test machine is my MacBook Pro 1.83GHz w. 2GB RAM, MacOS X 10.4.9, Apache 1.3 and PHP 5.2 (with Xdebug 2.0). I also have lots of applications running.
  • Peter Bowyer claims that require_once() is 3-4 times slower than require(). Ilia also says they are bad. My testing reveals the exact opposite with an empty include file. Callingrequire_once() 10000 times in a for() loop with an empty file is 4x faster.
  • Ilia advises against using magic functions like __autoload() and __get(), but the advantage of __autoload() in particular is obvious in any large project and is used by many phpframeworks. My primitive testing, however, shows inverse results. With a simply autoload requiring a class and 10000 loops of new Foo() versus require_once('foo.php'); new Foo() shows that __autoload() is ~3.7 times faster. I saw no difference between real methods and __get(), although the logic inside __get() will add some overhead.
  • If a class method can be static, declare it static. Speed improvement is by a factor of 4. I get a 50% speed increase (614ms vs. 414ms with 100000 iterations).
  • Avoid function calls within for() loop control blocks. In for( $i=0; $i<count($x); $i++ )the count($x) is called at every iteration.
  • Always, always quote array keys. $row['id'] is way faster than $row[id]. Ilia says 700%, I say about 200%.
  • Avoid regex if possible. Use ctype_digit($foo); rather than preg_match("![0-9]+!", $foo);.
  • Get rid of 'harmless' error messages - they take time to generate and output. The error supression operator @ is slow, so avoid when possible. With error_reporting set to E_ALL | E_STRICT on my machine, doing echo $rows[id] 10000 times instead of echo $rows['id]takes 38 times longer.

    UPDATE: To summarize, this slow code runs in 500ms (although this time will vary a great deal depending on your error_reporting level):






    $rows = array_fill(0, 10000, array('id'=>0));
    require_once('foo.php');
    for( $i=0; $i < count($rows); $i++) {
    foo::notdeclaredstatic();

    $rows[$i][id] = 0;
    }


    By using the techniques above, it can be made to complete in 68ms:
    $rows = array_fill(0, 10000, array('id'=>0));
    function __autoload($classname) { require_once( 'foo.php'); }
    $size = count($rows);
    for( $i=0; $i < $size; $i++) {
        foo::declaredstatic();
    
        $rows[$i]['id'] = 0;
    }
    10000 iterations is a lot for one request to a page. Using the techniques, the code became roughly 7 times faster.
    
    I am not out to prove Ilia wrong - he knows PHP better than most - and 
    for all I know, they could have optimized those very functions in PHP 5.2. I am, however, 
    interested in seeing what can be done to optimize PHP performance simply by doing things differently,
    by tweaking one's coding style. It would appear that there are improvements, albeit small, to achieve 
    from minimal effort. Plus I was surprised by the discrepancies I found compared to Ilia's recommendations.
    Hope this post helped you.
    Questions ?
    let me know by comments..
    :D

0 comments:

Post a Comment

Any Questions or Suggestions ?

About

Professional & Experienced Freelance Developer From India, Technologist, Software Engineer, internet marketer and Open Sources Developer with experience in Finance, Telecoms and the Media. Contact Me for freelancing projects.

Enter your email address:

Delivered by FeedBurner