Mobile Drupal optimization results

Our responsive redesign has been a great improvement for metaltoad.com. I was still not entirely satisfied with the speed of our site, especially while waiting for my train at the busy Pioneer Square!

One of the major obstacles for mobile networks is lag, and so I set out to cut down the number of HTTP requests. By improving the site's stylesheets and scripts, I was able to eliminate a dozen extra requests. Results were validated with PhantomJS and the OS X Network Link Conditioner.

hook_js_alter

First, I applied the hook_js_alter and hook_css_alter described in an earlier blog. Taking it a step farther, I replaced Drupal's built-in jQuery with a copy from Google APIs, and tweaked the sort order to get the fewest possible groups. That took us from four scripts down to just one, and six stylesheets down to three (all, screen, and print).

Data URIs with Compass

Since we're using Dan's Boilerplate theme with Compass, another easy step was to use the compass inline-image() function to replace some of our small images. Six more requests were removed this way.

By replacing url("images/dotted-line.gif") with inline-image("dotted-line.gif"), the compiled CSS becomes:

background: transparent url('data:image/gif;base64,R0lGODlhAwABAIAAAP///zIyMiH5BAEHAAAALAAAAAADAAEAAAICDFAAOw==') repeat-x left top;

Compass automatically keeps the data URIs up-to-date if and when the image file changes. For me, this is a major advantage of using Compass – image optimizations can be integrated into the workflow, instead of only happening when the front-end dev has time and Wheaties to spare.

Did it work?

To measure the results, I needed a way to take repeated samples of the page load time. Refreshing the page a few times isn't going to give an accurate picture, given the relatively high deviation in load time. PhantomJS (a headless WebKit interface) fits the bill. By running the test with the OS X Network Link Conditioner enabled, we can simulate a lossy 3G network. The simulated link was 780/330 kbps, with a 100 millisecond delay and 1% packet loss.

The result? Average load time time dropped by 10%, and the upper quartile was cut by 20%!

graph of load time

Source code

/**
 * Implements hook_js_alter().
 */
function mtm_custom_js_alter(&$javascript) {
  if (user_is_anonymous()) {
    // Move Google Analytics up so it can't break groups.
    foreach ($javascript as $name => $script) {
      if ($script['type'] == 'inline' &&
        strstr($script['data'], '_gaq.push')) {
        $javascript[$name]['weight'] = -1000;
        $javascript[$name]['group'] = JS_LIBRARY;
      }
    }
 
    // Replace JQuery with the same version from Google's CDN.
    if (isset($javascript['misc/jquery.js'])) {
      $javascript['misc/jquery.js']['data'] =
        '//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js';
      $javascript['misc/jquery.js']['type'] = 'external';
      $javascript['misc/jquery.js']['weight'] = -100;
    }
 
    uasort($javascript, 'mtm_custom_sort_css_js');
 
    $i = 0;
    foreach ($javascript as $name => $script) {
      $javascript[$name]['weight'] = $i++;
      $javascript[$name]['group'] = JS_DEFAULT;
      $javascript[$name]['every_page'] = FALSE;
    }
  }
}
 
/**
 * Implements hook_css_alter().
 */
function mtm_custom_css_alter(&$css) {
  if (user_is_anonymous()) {
    uasort($css, 'mtm_custom_sort_css_js');
    $i = 0;
    foreach ($css as $name => $style) {
      $css[$name]['weight'] = $i++;
      $css[$name]['group'] = CSS_DEFAULT;
      $css[$name]['every_page'] = FALSE;
    }
  }
}
 
/**
 * Custom sort order to optimize scripts and styles.
 */
function mtm_custom_sort_css_js($a, $b) {
  // Normalize the order of browser keys.
  if (isset($a['browsers'])) {
    ksort($a['browsers']);
    ksort($b['browsers']);
  }
 
  // Sort by integer weight values.  This allows explicit weights to break out
  // of a group, but the 0.001 automatically added to preserve insert order
  // cannot break a group.
  if ($return = ((int)$a['weight'] - (int)$b['weight'])) {
    return $return;
  }
  // Group common browsers together.
  elseif (isset($a['browsers']) && $return =
    strcmp(serialize($a['browsers']), serialize($b['browsers']))) {
    return $return;
  }
  // Group media types together.
  elseif (isset($a['media']) && $return = strcmp($a['media'], $b['media'])) {
    return $return;
  }
  // Finally, order by weight.  Multiply by 1000 to undo
  // the division in drupal_add_css.
  elseif ($return = ($a['weight'] * 1000 - $b['weight'] * 1000)) {
    return $return;
  }
  else {
    return 0;
  }
}

Want more awesome content like this? Subscribe to our newsletter!

Filed under 

Add new comment

Restricted HTML

  • Web page addresses and email addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <h4> <h5> <h6>
  • Lines and paragraphs break automatically.
  • You can enable syntax highlighting of source code with the following tags: <code>&lt;code&gt;</code>, <code>&lt;blockcode&gt;</code>, <code>&lt;apache&gt;</code>, <code>&lt;c&gt;</code>, <code>&lt;cpp&gt;</code>, <code>&lt;css&gt;</code>, <code>&lt;drupal5&gt;</code>, <code>&lt;drupal6&gt;</code>, <code>&lt;html&gt;</code>, <code>&lt;java&gt;</code>, <code>&lt;javascript&gt;</code>, <code>&lt;mysql&gt;</code>, <code>&lt;php&gt;</code>, <code>&lt;python&gt;</code>, <code>&lt;ruby&gt;</code>, <code>&lt;sql&gt;</code>, <code>&lt;xml&gt;</code>. The supported tag styles are: <code>&lt;foo&gt;</code>, <code>[foo]</code>.

About the Author

Dylan Tack, Director of Technology

Dylan is a software engineer with more than a decade of experience working with a wide variety of clients including the Linux Foundation, PBS, Habitat for Humanity, TV.com and the Emmys. His background includes training as an electrical engineer, but he became passionate about open source through his work with a university genetics lab.

Dylan is a proud member of the Drupal community, a member of the Drupal security team, and has extensive experience with Perl and Java. His other interests include computer security, embedded design, climbing, and brewing.

His latest talk at the Pacific Northwest Summit was titled: "Drupal Security for People Who Don't Care".