Optimizing PHP Through Habits
31st March, 2007 |
55 Comments
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, if for() 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. Calling require_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 php frameworks. 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.
« The Machine is Us
– Backup Your Mac to a Remote Location »
I think there's a flaw in testing require_once inside a for loop, rather than distributed throughout an application. In the for() loop example, it's bound to be faster because of not having the overhead of including the file. However most uses of require_once always include the file (it's used 'just in case'), and in these cases there's overhead.
Rasmus is certainly on record as saying it's slower, but for a different reason: bytecode caches cannot cache the results of a require_once call (or was it code with a require_once in - I forget).
Interesting article. Submitted in queue @ tweako ( http://www.tweako.com )
If you're purely looking for speed, then I can imagine require_once() is evil. But most people are more looking for quality. For preventing errors. If you include files twice, that will certainly give you errors. and require_once() prevents those.
Same for __autoload. If people say that is may be slower, fine. I'll trust them for their benchmarking. But using __autoload() will make your code so much cleaner, and by doing so, the code is easier to maintain. The impact of using __autoload() is, for me, not worth sacrificing maintainability. So it completely depends on the focus of your development. In my case, I develop for maintainability. Me having to spend a lot of extra hours on maintenance and development is more expensive than adding a server to a cluster, or simply buying a single machine for an application instead of using a shared machine. By the time the impact of using these functions is noticable, your site/application is quite popular and you'll probably have funding enough to get that machine.
Ther are many ways to prevent errors other than depending on require_once or include_once. The time for it has passed since many people in PHP world has learned from their work that OOP, design pattern wil work better for them than procedural coding style where people mess their code up with redundant require and include calls.
I believe __autoload() and require_once()'s benefit way surpass it's speed problem(in many cases, __autoload() can make things faster by only load the useful classes.
pcdinh: Funny enough, __autoload() makes redundant require_once statements completely a thing of the past, when people at least work in an OO way. So yes, I definitely agree, and work in this way. OOP and design patterns are probably one of the most important ingredients of the so much desired (at least by me) maintainability.
Joakim: Yes, I agree these two are free improvements. Not for my code though I've learned to work in that way (I guess that's a plus for the book and code that I learned PHP from).
Aside from statics, by the way, using Singleton pattern where appropriate will also save a lot, since those objects will only be instantiated one time.
The last example certainly have a faulty design, the code you should compare is following:
require_once('foo.php');
for( $i=0; $i < count($rows); $i++) {
foo::sta();
$rows[$i][id] = 0;
}
When it comes to require vs. require_once I would also like to see which code you used.
Your Mac makes a lousy test platform. In your test of require_once, I doubt the the OS calls ever actually hit the hard drive because of OS and hardware caches. On high volume web sites or even shared servers this will not be the typical result. Actually have to read from a disk is slow so eliminating requires and includes is much more important than which to use.
David Rodger
14th June, 2007
Coming late to this (sorry).
Could it be that require_once() checks the file's size?
I guess that is a true fact, how these things are executed faster.... but the thing is, why doesn't the PHP backend do these things for us? i think they should release a framework with PHP that executed the scripts faster by changing the way it does things. that would help tremendously.
thanks - eMarketing will be more and more important and php will help you on this way
Great and excellent article it’s realy helpful. Thanks again
Pardon me if I'm overlooking something here, but wouldn't looping require_once a thousand times result in the first require successfully executing and every subsequent call being passed over? Having required a file "once", as it were, it would seem an easy thing to fly through the rest of the loop's iterations saying "nope, already got that require", "nope, already got that require", "nope, already got that..." Well, you get the picture. *Not* doing something 999 times is a very different story than doing something 1,000 times.
Not to be rude, but it seems like a pointless test. Perhaps with a thousand uniquely named files it would test its true metal...
All that said, I do recommend using require_once over require or either include/include_once. If I'm counting on a file to be available within my code, by jimmy it better be there. Hence, require. And if I accidentally call for it within a loop, boy howdy--I'd better get it only once. Hence, require_once. ;]
Milliseconds be hanged; if my code is 'perfect', its worth being able to blink at least once while it runs.
David:.
Rasmus stated in his slides that opcode caches like APC don't play well with require_once and include_once (at the time - he also said subsequent versions of APC would address this problem automatically). To my knowledge, it has something to do with a stat() call that checks whether a file is already open, but with a cache, the file will not be open if it is already in cache, so it is a wasted step. Also, you can set the stat call to 0 in the php.ini. For Rasmus's presentation with audio, go here: http://www.niallkennedy.com/blog/2006/07/rasmus-lerdorf-php-web20.html
This site is interesting and very informative, wesele nicely interface. Enjoyed browsing through the site...
Milliseconds be hanged; if my code is 'perfect', its worth being able to blink at least once while it runs.
Not to be rude, but it seems like a pointless test. Perhaps with a thousand uniquely named files it would test its true metal...
Thank You Very Good Write..
Stefan, I completely agree with your views on using require_once and __autoload() as mentioned above. Maintainability is always a priority.
The point of the article was to investigate what performance gains could be achieved simply by making small adjustments to one's coding habits.
I think points like declaring methods static if appropriate and quoting array keys are two good examples of free speed improvements that, in fact, also improve maintainability.
Regin, the code was incorrect as you point out. However, the last example was meant to compare worst and best case.
I've updated the code for clarity.
arda turan
msn nickleri
Çok Güzel Hareketler Bunlar
kral tv
thanks for this post admin
Well all these tests from different people will only make sense if they were conducted on same computer specs.
However I would agree that PHP optimization should be part of coding habit.
Regin, the code was incorrect as you point out. However, the last example was meant to compare worst and best case.
I've updated the code for clarity. .:)
Thank you for sharing your article I would always follow..
film izle film izleme sitesi, bedava film izle bedava film izleme sitesi, online film izle online film izle sitesi, sinema izle sinema izleme sitesi, divx film izle divx film izleme sitesi
thanks you
You will have to crawl very nice,owe you gratitude..
Document content and think of all the information very useful.
Document content and think of all the information very useful. Vektör
Grafiker
Mimar
Al??veris Sitesi
video izle
Post A Comment
You need JavaScript to post comments.