One (opcode) cache to rule them all?
First off, let me say this - Wincache FTW!
OK – with that off my chest, I can get down to business. This post is a long time in the making. I’ve spent a considerable amount of time over the past few months testing numerous site configurations (Windows/IIS versions, PHP, mySQL, WordPress plug-ins, etc.) to find out which will run best for me. In short, it’s been nothing short of a learning experience – many times very frustrating but finally very rewarding.
When I originally put up the site, I was steadfast in my commitment to having everything running on 64-bit Windows. That meant that I had three choice to deploy on:
- Windows 2003
- Windows 2008
- Windows 2008 R2
I have nothing against Windows 2003, but that platform is getting a bit long in the tooth at this point and wasn’t really where I wanted to be long-term. Windows 2008 seemed like a viable option, but Windows 2008 R2 is superior in many ways, and the code ‘feels’ cleaner and it just seems to run smoother and is more refined at every turn, so Windows 2008 R2 it was.
With my OS of choice selected, I could then focus on everything else needed to run WordPress on Windows and IIS. At the time of the initial install, I was able to find a x64 build of PHP 5.2 and x64 mySQL so I figured that I had everything I needed. My initial server configuration worked really well and the install of WordPress was uneventful and outside of getting some file system permissions sorted out (mainly to support plug-in upgrades and picture uploads), everything was as it should be. I proceeded to run on this configuration as ‘production’ for several months while I was learning more about WordPress and PHP as a platform.
After spending a considerable amount of time researching WordPress plugins, and trying out many, many different ones, I began to settle into a list of core WordPress plugins that I wanted to run. The only downside of all of the wonderful plugins is the performance toll that they exact, and I could tell that the site was starting to bog down a bit due to the number – and types – of plugins I was running. After doing all of the ‘normal’ OS optimizations (data separation onto multiple spindles, RAM, etc) I had to find something to get the site performance back up to acceptable levels. I found a few WordPress caching plug-ins and after trying them out, I settled on using W3 Total Cache. One of the nice things about this plugin is that it supports a wide range of options (including advanced disk caching and multiple levels of caching within WordPress) and it introduced me to concept of PHP opcode caching. The more I read about such, the more I came to the conclusion that an opcode cache is a necessity on any production site and reasoned that this would be a good solution (or at the very least a partial solution) to my performance issues.
So I proceeded to try every PHP opcode cache I could find to learn about them and better understand the advantages/disadvantages of each… eAccelerator, xCache, APC and memcached were all installed (well, in memcached’s case, it was just read about), configured and put through their paces, and all with disappointing results. I think that the fact that I was running 64-bit IIS and one of the unofficial 64-bit PHP distributions with the 32-bit opcode caches that led to most of my issues. Let’s face it – the opcode caches were initially built and optimized for running with Apache on Linux – I quickly found out that the opcode caching situation on Windows and IIS was definitely not high on anyone’s radar (until Wincache came along…. remember the FTW?).
Regardless, I pressed on, and after installing each of the opcode caches in turn and getting them configured it seemed that everything was running fine at first… maybe not much faster, but everything seemed to work until I would try to access a different site (outside of the first site loaded after IIS was reset) and then the PHP executable (via FastCGI) would tank and IIS would recycle the site’s application pool and eventually everything would recover. It actually took me a while to notice this since I would rarely visit the other sites running on my webserver as they are used mainly for maintenance and utility functions.
My site is the only ‘full’ site running on my webserver, so I would only see this behavior if I used another PHP application – like phpMyAdmin (running on a different site within IIS) – and then flipped back over to brokentoken (or vice versa). It was as if whichever site was loaded by the webserver first would work fine, and all other PHP sites on the server would fail with ‘php-cgi.exe’ abending within IIS. If I removed the opcode cache from my PHP configuration, reset the webserver and then test again, everything worked just fine and no unexplained crashes were seen for the duration under that configuration. Fearing it might be the 32/64-bit mix of code coming into play and the fact that the opcode caches were never built for a 64-bit environment, I decided to punt.
As an experiment, I installed a test 32-bit Windows 2008 box and lifted my WordPress/PHP/mySQL configuration over to it to see if the opcode caches performed any better in a pure 32-bit environment with IIS. Unfortunately, they did not. It seemed that any of the opcode caches running in a FastCGI configuration were experiencing problems, and because going back to using an ISAPI extension for PHP seemed untenable, I felt stuck and without a good and reliable solution for IIS.
I was honestly a bit deflated. I felt like I had failed in my commitment to getting my desired configuration working, and besides… it just didn’t seem *that* impossible, so I figured I would side-load the server as much as possible and optimize everywhere else and forego an opcode cache altogether. Fortunately I didn’t have to do that. After poking around the intertubes a bit more, I came across Wincache. As it turns out, this PHP opcode cache is coded and maintained by Microsoft and is specifically built for use with IIS. It sounded almost too good to be true. At this time, Wincache is only available in x86 format, so I stuck with my Windows 2008 installation and proceeded to go forward with my testing.
After running it for a while, all I can say is ‘WOW’. It really works, is fast and fully supports PHP in a FastCGI configuration and runs just fine against multiple sites on the same server and doesn’t seem to suffer from any of the aforementioned issues that I would see when running the other caches. My page load times (measured using the method documented here) dropped from 1-2 seconds (with no opcode cache being used) to less than 0.5 seconds (with Wincache running) and it made a very perceivable change in how ‘snappy’ the site appears to be. The cache management and statistics pages that Microsoft supplies with Wincache are also very nice. I highly encourage anyone wanting to optimize PHP on IIS to give Wincache some serious consideration.
I know I have a lot of additional work to do on the site to further optimize it even more, but having the opcode cache running (and working without abending) is a big win and it made a huge difference very quickly – definitely a big score.
If I could wish for a few things from Wincache, it would be this:
- Provide support for an x64 environment (hopefully this will happen after PHP is available in an official x64 build)
- Fix the file-change notification issue that plagues WordPress plug-in upgrades.
- provide whatever hooks might be necessary to allow Wincache to be seen as a valid cache operator for the other opcode-aware caching plug-ins. I realize the plug-in authors will have to update their code as well and I hope to see such done at some point soon.
All-in-all, I must give a huge ‘Thank You!’ to Microsoft for developing this and putting it out for us ‘PHP on IIS faithful’ to use. It solves a huge problem for us WordPress fans wanting to host on Windows. I’m sure the other opcode caches work very well with Apache and ‘not Windows’, but I don’t really feel like switching my operating system and my webserver of choice for just for one specific feature.
Come on (official) x64 PHP… it can’t happen soon enough!