Drupal 8 performance: enabling the classloader cache

Drupal 8 performance: enabling the classloader cache

Difficulty: 
Piece of Cake

Drupal 8 is using some of the PHP language features that make PHP a "more or less decent" programming language and one of them is Autoloading. But because autoloading in PHP leaves the real autoloading job to userland code this can easily have very nasty performance implications.

If you want to use Autoloading now in Drupal 7 check out the extremely useful XAutoload module.

Understanding the basics of how autoloading works on PHP

1. You ask PHP for a class, for example, by trying to create an object:

$datetime = new \ms\System\netDateTime();

2. PHP will look for that class name in the scripts that it has already loaded in memory. If it finds something, you get your object.

3. If it fails to find anything it will start calling the registered autoloaders until one of them is able to - given the full qualified class name - figure out what file is that class contained in and include it into the running script. Yes this is not a typo: class name resolution / autoloading is a userland task in PHP.

PHP has a quite flawed namespace support, but at least it's got something. Like more or less any other PHP feature.

Another thing to consider is that because Drupal 8 is a system over a system over another system - guess why it performs the way it does - you've got some additional autoloading burden when trying to figure out how to resolve a full qualified class name into a php file that can be included into the running script.

Enabling the classloader cache

To speed up the autoloader job what we are going to do is to have our class loader cache the results of class name resolution so that this task needs only to be done once.

If you browse Drupal 8 settings.php code you will come accross this commented piece of code:

/*
if ($settings['hash_salt']) {
  $prefix = 'drupal.' . hash('sha256', 'drupal.' . $settings['hash_salt']);
  $apc_loader = new \Symfony\Component\ClassLoader\ApcClassLoader($prefix, $class_loader);
  unset($prefix);
  $class_loader->unregister();
  $apc_loader->register();
  $class_loader = $apc_loader;
}
*/

Symfony has several class loader implementations that leverage different platform specific caching capabilities such as:

\Symfony\Component\ClassLoader\ApcClassLoader
\Symfony\Component\ClassLoader\WinCacheClassLoader
\Symfony\Component\ClassLoader\XcacheClassLoader

So what you need to do is to replace the default classloader with one that leverages caching for the platform you will be deploying to in settings.php:

if ($settings['hash_salt']) {
  $prefix = 'drupal.' . hash('sha256', 'drupal.' . $settings['hash_salt']);
  $loader = new \Symfony\Component\ClassLoader\WincacheClassLoader($prefix, $class_loader);
  unset($prefix);
  $class_loader->unregister();
  $loader ->register();
  $class_loader = $loader ;
}

There is an issue in Drupal.org to add support for automatic detection of the best suited Symfony class loader on different platforms.

As per this benchmark (by Wim Leers) the expected performance gains are up to 24% more request per second on some use cases.

 

 

Add new comment

By: root Tuesday, October 27, 2015 - 09:00