Horde Hackathon 2011 part 2
Part 2 of my personal programming projects during the Horde Hackathon 2011 in Boston, featuring an autoloader cache which boosts performance a bunch.
Autoloader caching
It shouldn't be a suprise to any administrator these days that it's a very good idea to install a bytecode cache for PHP if you care about performance, especially if using modular frameworks like Horde. Or any other modern framework. This alone already gets you some impressive performance boost.
While profiling Horde applications I noticed that even after enabling a bytecode cache, a lot of the time is still spent in the autoloader code. It's not spent with loading the class files, but with mapping class names to file system paths. This is not news either and it's the reason why one solution is to provide static class maps. Since most of this time is spent for stat operations on the file paths, disabling stat helps a lot too, at least if possible in your cache of choice.
These solutions don't work well for us for several reasons, so I was looking for an alternative approach. I played with adding caching functionality to Horde's autoloader library. The results have been great. There was one problem though. The autoloader is created as one of the very first actions during a request, long before any configuration is available. I had a few must-haves in mind:
- It should work dynamically, i.e. with no interaction necessary on the developer's or adminstrator's end when adding new classes (rules out static maps).
- It must not break or require interaction when upgrading libraries or applications (rules out apc.stat).
- It should work with as many cache backends as possible, autodetecting which are available, without configuration (rules out Memcache).
- It must be able to turn it on and off without configuration.
Here's what I've come up with: for broadest backend support (APC, XCache, eAccelerator), I check for any supported extension that doesn't require configuration, and fall back to a file system cache, e.g.:
if (extension_loaded('apc')) { $this->_cache = apc_fetch('horde_autoloader_cache'); } elseif (extension_loaded('xcache')) { $this->_cache = xcache_get('horde_autoloader_cache'); } elseif (extension_loaded('eaccelerator')) { $this->_cache = eaccelerator_get('horde_autoloader_cache'); } elseif (($tempdir = sys_get_temp_dir()) && is_readable($tempdir . '/horde_autoloader_cache')) { $this->_cache = @json_decode(file_get_contents( $tempdir . '/horde_autoloader_cache'), true); }
But how to enable and disable it without any configuration? I made this a standalone package that extends and replaces the default Horde autoloader...
spl_autoload_unregister(array($__autoloader, 'loadClass')); $__autoloader = new Horde_Autoloader_Cache(); $__autoloader->registerAutoloader();
...and is only loaded if it can be found in PHP's path:
if (!@include_once 'Horde/Autoloader/Cache.php') { require_once 'Horde/Autoloader/Default.php'; }
To enable the cache the adminstrator excutes:
pear install horde/horde_autoloader_cache
And to disable it:
pear uninstall horde/horde_autoloader_cacheAdd some simple console script to empty the cache, if possible. Not all cache extensions work with the PHP CLI SAPI.
Voilą. Thanks to a few lines of code, the great work of the different cache extension projects, the simplicity of installing packages with the PEAR installer, and the easy extendability of the Horde libraries and framework, you get a magnitude of performance boost for free.
A first release candidate of the package will be released tomorrow.
Bits and pieces
A few small things that I've also worked on during the Hackathon:
- The daily calendar agenda that can be sent from Kronolith includes the event status now, so you don't get confused by cancelled or free events in your agenda.
- Deleting events from the dynamic calendar view shows an intermediate confirmation screen now, just like the traditional view, if enabled in the user preferences.
- A work week view contributed by a long-term Horde user has been integrated into the dynamic view. This will be available with the next (non-bug-fix) version of Kronolith.
- A whole slew of bug fixes.