13

I'm looking for tips from other Drupal developers to make Drupal faster when developing (specially Drupal 7), taking in consideration that during development you don't want cache, so you can see the result of changes in code PHP/CSS/JavaScript. (See some answers about flushing cache / disabling cache permanently.)

Googling for "drupal slow localhost" will give some tips. but I want yours.

2
  • Which database (engine) do you use? Aug 19, 2011 at 20:46
  • can confirm Drupal or MySQL is very slow in premium hardware PC
    – mevsme
    Nov 21 at 19:05

6 Answers 6

3

You can probably get further by changing how you work instead of making Drupal faster on page loads.

The main thing here is using Drush to do stuff like clearing the cache (Even when disabling the page cache, there is still dozens of different caches involved, for example for hook implementations), installing/re-installing modules and so on.

And of course, APC should be installed, yes. You could even think about using http://drupal.org/project/apc to store parts of the cache in APC. This will however conflict with drush (you can't clear the APC cache by using drush) and will require additionaly RAM.

2
  • 1
    Thanks, I'm aware of Drush. (and that's not the question) But anwyay in the case of "drush cc" I think it's faster the admin_menu module shortcut (clear caches + refresh in only 1 click)
    – corbacho
    Mar 25, 2011 at 9:45
  • That won't be the case if your web interface is loading slowly. In that situation the time it takes to activate a feature (clear caches/refresh) may only take one click, but it might take many seconds to get to the activation screen due to slow loading by your web server. The advantage of drush is it is decoupled from this bottleneck. Feb 15, 2012 at 14:15
5

The best tip you can use (this really change my life) is adding a line in your my.cnf file:

innodb_flush_log_at_trx_commit = 0

You can find this file in your wamp folder or if your on linux server: /etc/mysql/my.cnf

Regards

1
  • I hadn't dealt with innodb_flush_log_at_trx_commit until today, but changing it to 0 just changed the ETA for an import from 28+ hours to < 1 hour! You can find more details about the command on the MySQL website: dev.mysql.com/doc/refman/5.5/en/…
    – Matt V.
    Jun 26, 2012 at 18:19
3

Install and enable APC. As I tested in Drupal 7, this will make your local site faster.

1

I updated this Drupal Tutorial with some of the solutions I found:

Speed up page loading time on localhost: http://drupal.org/node/961012

1

Thought I'd post my experience which may help future readers. I struggled with for over 24 months with a really slow Drupal 7.3x stack on an Ubuntu 15.04 desktop (I just couldn't be bothered to with more pain and just carried on!). Today, I took a stab at installing MySQL Workbench 6.1 and it now flies by! Unbelievable!! I hope any future readers will consider this first as none the suggestions offered really solved my issues. The machine I use is a fair decent spec (16g RAM/dual core/2TB/Dual-head etc), so buying/getting 'another computer', I don't believe is the true answer. In all, if you are running Drupal on a desktop then try the above Workbench install first. To date, I've found I need to run Workbench in the background (or on another screen), still my whole Drupal experience has now improved immensely. Maybe I have just been luck today? Anyway, hope this helps future readers.

0

IMPORTANT!!! If you want to use this solution do it with caution and provide a constant that can flag the caching code on/off.

The solution I show here is not of settings or tuning of some settings, but do some change in the code of the core in order to make Drupal 7 works much faster.

During development I noticed that many request, even the most simply ones has a latency of 5 seconds. This is frustrating, especially when you need to do lots of settings such as editing views.

At first I thought that the problem is related with general issue found by many developers when working with Apace on local host, but changing one solution after another didn't changed the slow behavior.

I decided to debug, and then step by step I narrowed down the code section where most of the delay (4 seconds) occur.

I an using with ECK module to create entities for business-logic. And I have quite few entities. The latency in my case was caused here:

eck.module :: eck_entity_info and especially in the foreach loop when collecting entity info from all hooks into one array:

  foreach (EntityType::loadAll() as $entity_type) {
    // eck__entity_info creates the entity_info for each entity type.
    $info = array_merge($info, eck__entity_type__info($entity_type));
  }

I took some measurements and noticed that there is no one single module that cause the delay but few modules create a compound time of 4 seconds added to each new request that use this entity_get_info function.

What I did is to create a caching file with serialized result of the foreach loop. The file is created once and deleted by the eck_flush_caches hook implementation. This solution eliminated the latency caused by this section in the code. Now a simple GET request that took 7 seconds has become 2.4 seconds.

I took this solution one step ahead and instead of caching only the result for eck_entity_info, I moved the caching to the top level when all entity info from all modules is collected in file: common.inc. Now the request time was reduced to 1.3 second!!! Using Drupal is fun again.

Note: I am not saying that this solution is good for all but in the case that you are using many entities and the settings for these entities are not dynamically change every request, you may want to consider this solution.

I am attaching two changes made in common.inc file:

function entity_get_info($entity_type = NULL) {
  global $language;

  // Use the advanced drupal_static() pattern, since this is called very often.
  static $drupal_static_fast;
  if (!isset($drupal_static_fast)) {
    $drupal_static_fast['entity_info'] = &drupal_static(__FUNCTION__);
  }
  $entity_info = &$drupal_static_fast['entity_info'];

  // hook_entity_info() includes translated strings, so each language is cached
  // separately.
  $langcode = $language->language;

  if (empty($entity_info)) {
    if ($cache = cache_get("entity_info:$langcode")) {
      $entity_info = $cache->data;
    }
    else {
       if (defined("CACHE_GET_ENTITY_INFO") && CACHE_GET_ENTITY_INFO && defined("CACHE_DIR")) {
          $cacheFile = CACHE_DIR . "/entity_get_info.phps";
          $cachedData = file_exists($cacheFile) ? file_get_contents($cacheFile) : false;
          if(! $cachedData) {
              $entity_info = module_invoke_all('entity_info');
              file_put_contents($cacheFile, serialize($entity_info));
          } else {
              $entity_info = unserialize($cachedData);
          }
      } else {
          $entity_info = module_invoke_all('entity_info');
      }


      // Merge in default values.
      foreach ($entity_info as $name => $data) {
        $entity_info[$name] += array(
          'fieldable' => FALSE,
          'controller class' => 'DrupalDefaultEntityController',
          'static cache' => TRUE,
          'field cache' => TRUE,
          'load hook' => $name . '_load',
          'bundles' => array(),
          'view modes' => array(),
          'entity keys' => array(),
          'translation' => array(),
        );
        $entity_info[$name]['entity keys'] += array(
          'revision' => '',
          'bundle' => '',
        );
        foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) {
          $entity_info[$name]['view modes'][$view_mode] += array(
            'custom settings' => FALSE,
          );
        }
        // If no bundle key is provided, assume a single bundle, named after
        // the entity type.
        if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) {
          $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label']));
        }
        // Prepare entity schema fields SQL info for
        // DrupalEntityControllerInterface::buildQuery().
        if (isset($entity_info[$name]['base table'])) {
          $entity_info[$name]['base table field types'] = drupal_schema_field_types($entity_info[$name]['base table']);
          $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']);
          if (isset($entity_info[$name]['revision table'])) {
            $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']);
          }
        }
      }
      // Let other modules alter the entity info.
      drupal_alter('entity_info', $entity_info);
      cache_set("entity_info:$langcode", $entity_info);
    }
  }

  if (empty($entity_type)) {
    return $entity_info;
  }
  elseif (isset($entity_info[$entity_type])) {
    return $entity_info[$entity_type];
  }
}

The section of code where cache file is create is this:


if (defined("CACHE_GET_ENTITY_INFO") && CACHE_GET_ENTITY_INFO && defined("CACHE_DIR")) {
          $cacheFile = CACHE_DIR . "/entity_get_info.phps";
          $cachedData = file_exists($cacheFile) ? file_get_contents($cacheFile) : false;
          if(! $cachedData) {
              $entity_info = module_invoke_all('entity_info');
              file_put_contents($cacheFile, serialize($entity_info));
          } else {
              $entity_info = unserialize($cachedData);
          }
      } else {
          $entity_info = module_invoke_all('entity_info');
      }

The constant "CACHE_DIR" is a constant that refer to cache directory in my solution. Make the adjustments for this as necessary.

Then the cleanup if the cache file:

function entity_info_cache_clear() {
  drupal_static_reset('entity_get_info');
  // Clear all languages.
  cache_clear_all('entity_info:', 'cache', TRUE);

  //Gilad: clearing the cached data of entity info
  if (defined("CACHE_GET_ENTITY_INFO") && CACHE_GET_ENTITY_INFO && defined("CACHE_DIR")) {
     $cacheFile = CACHE_DIR . "/entity_get_info.phps";
     if (file_exists(CACHE_DIR . "/entity_get_info.phps")) {
         unlink($cacheFile);
     }
  }
}

Thoughts for future... Taking this solution for the next level would be adding caching in the level of **function module_invoke_all($hook) **

One of the great features of Durpal is the flexibility provided by allowing hooks to extend the CMS logic. However the same feature may be a major cause for performance issue. Basically if you have an algorithm that build setting, but running this algorithm again and again for each request produces the same result, one may want to consider caching the result produced by algorithm and serve it from file.
Therefore adding an ability to cache the result produced by running hooks on many modules cab speed up business logic dramatically.

Here is a conceptual solution:

function module_invoke_all($hook) {
  $args = func_get_args();
  // Remove $hook from the arguments.
  unset($args[0]);
  $return = array();


  $doCaching = $is_hook_result_to_be_cached($hook);
  if($doCaching) {
      $cacheFile = CACHE_DIR . "/{$hook}_cache.phps";
      $cachedData = file_exists($cacheFile) ? file_get_contents($cacheFile) : false;
  }
  
  if( $cachedData) {
      return unserialize($cachedData);
  



}

  foreach (module_implements($hook) as $module) {
    $function = $module . '_' . $hook;
    if (function_exists($function)) {
      $result = call_user_func_array($function, $args);
      if (isset($result) && is_array($result)) {
        $return = array_merge_recursive($return, $result);
      }
      elseif (isset($result)) {
        $return[] = $result;
      }
    }
  }


  if($doCaching) {
     $cacheFile = CACHE_DIR . "/{$hook}_cache.phps";
     file_put_contents($cacheFile, serialize($entity_info));
  }

  return $return;
}

Of course the function $is_hook_result_to_be_cached($hook) need to be created and defined, maybe involve some settings level in the admin section.

P.S. consider that all hosts are running today with electronic discs (and P.C uses SSD), therefore using file caching is a good and available solution. Of course other ways for caching can also be considered.

Not the answer you're looking for? Browse other questions tagged or ask your own question.