Speed up your Joomla! (Or any CMS, really)
Wow...I didn't know that there were still PHP/MySQL sites (let alone those powered by Joomla! 1.5) that were taking *any* appreciable time to generate nowadays. Until a client came to us saying that his Joomla! 1.5 powered news site was taking 7 to 10 seconds, to generate a page, and up to 20 seconds to download the page.
Suspecting that this was merely a *really* bad service provider, I tried the old standby .htaccess tricks to get things up and running a bit quicker:
<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)(\.gz)?$">
#Set everything that *probably* isn't dynamically generated to
#be cached by the browser for two weeks
Header set Expires "access plus 2 weeks"
#Unset ETags for everything that *probably* isn't dynamically
#generated
Header unset ETag
FileETag None
</FilesMatch>
# Gzip all text-based files
<FilesMatch ".(php|html|css|js)$">
SetOutputFilter DEFLATE
</FilesMatch>
Now, the quick answer to the question "That's pretty, but what does it all mean?" is:
- By setting the header "Expires" to "access plus 2 weeks" means that (as long as the browser's cache isn't cleared), when the browser returns to that site, it will first ask if file xyz.GIF has been modified since it first requested it. If not, it will use it's cached version. If it has, the server will intelligently send the file, in stead of your browser having to request it a second time.
- Unsetting ETags and setting ETags to none is a mystery to me, honestly, because one would assume doing one or the other would suffice...but, whatever. According to Yahoo!, sites who do not use eTags are more free to rearrange their content how they like; and if Apache doesn't have to spend time calculating a file's eTag, it's free to do other things. An eTag seems like a really good idea at first blush - an absolute unique identifier for a file. But, being calculated on the fly and not stored in memory, either of which consume resources, slows things down.
- By using the DEFLATE method for text based (usually CMS generated HTML/CSS/JS) and gzipping all content sent to the browser, you can take a 200 KB file and shrink it by half (or better, depending on your settings in httpd.conf). There are tweaks for this, if you have a large number of IE5/6 or Netscape 3/4/5 users. If not, don't worry. I don't, so I'm not wasting my time by explaining. Visit our site in IE5 or IE6 and you'll get a purposefully broken site with a nag screen to upgrade your browser.
Now, thinking I'd taken care of the problem and feeling slightly smugly superior, I uploaded the renewed .htaccess file (all of my stuff before the CMS generated items to make sure that things would get overridden, if there was a Joomla! addon to do so. I cleared my cache, opened Chrome's Developer Tools to the "Resources" tab and hit Command+R (I'm on a Mac, I think PC users hit CTRL+F5) and voila...the booger's still taking 6 seconds to generate a page and twelve to download it - almost 20 seconds. Realizing my cache wasn't primed (my bad for clearing it), I hit Command+R again...still 6 seconds to generate, but luckily only 4 to download the page and all it's graphics this time...after waiting 6 seconds to generate. So, now I'm down to about half, and the customer thinks it's great.
I don't give up so easily. Six seconds, in today's information hungry society is the difference between a click-through to your site and someone giving up on the Google result and going to someone else's site (for their, undoubtedly, inferior information).
Luckily, this customer was in the process of moving his site to a new server. Drool. New server - it still had that new server smell. And, when I shelled into using 'root' the very first time, the "history" command, had ten entries. Ten. Ahhh, new server.
You may or may not be able to use the tips I'm about to give you to install APC, based on your hosting environment; I do know that if you manage your own *nix server, you should be ale to pull these off. If you use Media Temple's Grid Server (gs) shared hosting, there are instructions for how to install APC in the support FAQ.
Generally, if you have PECL and PEAR installed (most new PHP installs do this for you nowadays, rather than having to do it on your own), all you have to do is issue the
pecl install APC-beta
command from the command line. As of the time of this writing, the APC-beta is version 3.1.3p1. This has a VERY important addition which finally makes using APC worthwhile in the PHP5 arena. Basically, prior to 3.1.3, you couldn't use the __autoload() feature efficiently with APC - meaning any classes you autoloaded (or as some people call it "lazy load") would not be cached by APC's opcode cacher, meaning that the code was evaluated time and time again. This is not good for production environments, where as much as 90% of modern CMS's are 'lazy loaded'. Joomla!, while I'm not sure of the exact number, falls into this group of CMS's and definitely, until late 2009, did not benefit fully from an opcode cacher.
Once APC is installed, check your php.ini file for the following entries:
extension="apc.so"
apc.enabled = 1
apc.shm_size = 128
apc.include_once_override = 1
apc.mmap_file_mask = "/tmp/apc.XXXXXX"
apc.shm_segments = 10
You'll need to match everything up to your hosting environment and make sure things work out for you, but I've found that this is an über generous setup for our server here. Basically, this gives our server ten shared memory segment threads, 128MB of shared memory (RAM) to play with and access to the /tmp directory for overflow. Your site may need more or less threads, but tweak it and see what works for you.
Now on to the magic with Joomla! - make two changes to your configuration file, 'round about line 24 (yes they can be made through the admin interface, but if you're already at the command line, why not just nano '/home/mysite/public_html/configuration.php'?), change this:
var $cachetime = '20';
var $cache_handler = 'file';
to this:
var $cachetime = '180';
var $cache_handler = 'apc';
At this point, I'm going to step back and say, no matter what your temptation may be to do so, under no circumstances, NONE, mind you, are you to mess around with the session handler. Even though the Joomla! team may lead you to believe that all session management is routed through their class structure, it is not. If you set the session manager to anything other than database or file, kiss your site goodbye until you change it back, through editing the configuration file. It just does not work, forget about it.
The change that we did just make tells Joomla! (and the majority of non-legacy modules, plugins, etc) to utilize APC for caching anything they might need to cache. And it actually works really well. In-memory caching reduces seek times by over 90% and took our customer's site from 6 seconds per page to generate, to just over 2. The rest of the problems lay in the database and we're seeing if a separate server takes care of that (10,000+ records in a MyISAM table with no indexes...oi veh).
Now on to one further optimization tool that does not get it's fair amount of press, that comes in the form of a Joomla! plugin (but using the base class structure is really for any site):
Meet PHP Speedy. It's like crack-laced-methamphetamine with a PCP chaser for your website. Basically, PHP Speedy takes your JS & CSS, refines it down to the smallest size it can and spits it out in one file for CSS and one file for JS. Our customer had no less than four external JS files on each page and six external style sheets. Thanks to the PHP Speedy Plugin (found here for Joomla!) There's now one CSS file per page and one JS file per page. Coupling that with our .htaccess rules from earlier, pages now take a total...drum roll please...of four seconds from request to display. We went from:
- .5 seconds for request
- 5 seconds to create page
- .5 seconds to return page
- 10 seconds + for all page content
- .5 seconds for request
- 2.3 seconds (average) to create page
- .5 seconds to return page
- 1.2 seconds (average) to return all content (to a primed browser cache)
