“Micro” Optimizations That Matter
Out Of Date Warning
Languages change. Perspectives are different. Ideas move on. This article was published on October 16, 2009 which is more than two years ago. It may be out of date. You should verify that technical information in this article is still current before relying upon it for your own purposes.
Every few weeks, someone publishes an article talking about how it’s faster to use single quotes rather than double quotes and how you should use echo() instead of print(). Most of these are bunk; that is, the time we spend talking about them far exceed the CPU time saved by implementing them.
Micro optimization doesn’t work. So why, then, is this post called “micro optimizations that matter”? The optimizations below could be described as micro – not in the little amounts of performance improved, but in the very minute (if any) changes required to your code to make use of them. All of these optimizations are standard optimizations you should consider, and all of them will offer considerable performance enhancements.
The fastest, easiest, and least painful way to get a performance boost is to enable caching on your server. There are a number of caches that are available to you.
First, if your database query cache is turned off, turn it on. For MySQL, that means taking a look at the documentation and setting up the query cache to cache all queries. This helps because it prevents the database from having to rerun queries. You should also have an opcode cache installed. I like APC. APC will automatically cache the opcodes from the compilation of your scripts. There are ways to boost the the performance of APC that you can investigate as well.
Neither of those to suggestions requires any code changes on your part, but will yield improvements in performance, sometimes as great as 300% (where APC is concerned). These “micro optimizations” are crucial. There are also some things you can do with caching that will require code changes but will make your application better for them.
The first is to enable the use of either APC or Memcached to cache objects and data points. For example, there’s no reason you should even be asking the database (regardless of the enabling of the query cache) to generate your blog post list every time someone visits your blog. Put that in the cache. You can even put your sessions into memcached to eliminate disk IO. These will require some code changes, but will be worth it.
Eliminate Any Sort Of Logged Errors
Disk IO is one of the things that can kill your application’s performance. Disks are usually very slow; people pay attention to how large a disk is but not how fast it is, and memory is always faster. Unfortunately, one of the ways that people kill their apps is by logging unnecessary warnings, errors and notices. I say “unnecessary” because they’re things that should have been resolved before the application went into production. Make sure that you get rid of notices that are avoidable and only have errors raised when something truly does go wrong.
Enable Output Buffering For Everything
It is possible to set an INI command that will enable output buffering on all of your pages. This is a good thing, because it means that Apache will get the parsed version of your PHP application as a chunk, rather than piecemeal, improving performance and reducing system calls (see this PDF for more).
You can turn on output buffering in the php.ini file with the following directive:
Will output buffering solve your problems if your application is resource-intensive or badly written? No. But it will help improve the performance of a well-written application.
Make Use Of A Content-Delivery Network
One of the fastest and easiest ways to reduce the total load time of a page and the load on your server is by moving things like images and videos to a dedicated server or content delivery network. For example, you can make use of Amazon’s S3 service. This low-cost service will allow you to have another server provide images, reducing the load on your own server quickly, cheaply, and without too much code modification. Less load on Apache means that you have the ability to serve more pages.
Determine What Data Doesn’t Need To Be Real-Time
If you are still pinging the database on each request, you’re doing it wrong. No seriously. Chances are good that if you’re like 99% of other people, you have some data that can be stale. By “stale” I mean “not updated every request.” For example, the comments count on your blog might be something that can be stale. Or the actual comments themselves. Or maybe the popularity of posts. Or perhaps a counter or some other dynamic content. See what you can make stale versus real time, and use that to reduce load.
Allowing data to get stale reduces load because instead of having to ping the database, the server can just serve up what it already knows. Combined with memcached or APC, and you can keep from even having to do a disk read to get your data. That’s going to result in potentially significant performance improvements.
Consider Using An Autoloader
Autoloaders are something we’ve discussed before. The SPL Autoloader allows you to create a stack of autoload functions that are looked at when you invoke a class that doesn’t exist in the current scope, and tries to load that class.
Autoloaders can result in performance improvements. Here’s why: each time you include a file, that file has to be compiled. If you include six files, that’s seven times the compiler has to be run (one for each included file, and once for the file you’re executing). APC improves this, but only to a degree; you’re still running the stat() calls and other junk that goes along with it. If you’re using all seven files, fine. But if you’re not, you’re wasting CPU time compiling code that’s sitting there.
This, perhaps, takes the most work, because it requires you to redefine your application in classes and objects, using OOP. This certainly is not for everyone. However, you can gain significant performance improvements through this method.
Some Additional Notes
There are some things that definitely do not make your code faster, even though people sometimes argue that they do. For example, less code does not necessarily equate to more speed. This might seem like a paradox, but it’s true: reducing lines of code does not necessarily have an impact on the speed of your application.
It’s good to refactor and reduce the lines of code being used, but this should not be part of your performance strategy, or your optimization strategy. It should be part of your code cleanliness strategy.
It’s always a good idea to architect first, and optimize later. Lots of times, you can get enhanced performance just by improving the architecture of your application, rather than implementing these or any other strategies. You should always solve the business case for optimizations, too. Make sure you know why you’re doing them, and what you’ll gain. And make sure that you go through a process before you just start mucking around with your code. Good luck!