How-to library · Developer docs

How-to library

Task-shaped pages: a title that starts with a verb, the situation in a few lines, the steps, the working code in both engines, and why this way rather than another. Found from a search, done, and left.

Find out why a page is slow

Cached or rendered, server or browser, then which part of the template: three measurements that find the cause of a slow page before anyone changes code.

Context

Context. Someone reports that a page is slow. The cause is one of a few things: the page is never served from the cache, its render does too much work, or the browser has too much to download. Guessing usually fixes something real that was not the slow part. These steps measure first, narrowing from the whole request to one section of one template, and the last step leaves a switch in the template that makes the narrowing quick.

Steps

  1. Cached or rendered? Request the page twice signed out and read X-Cache-Status. A second miss means every visitor waits for a render; find out why with the diagnostic before anything else.
  2. Server or browser? Compare the time to first byte of a render with that of a plain page on the same site. If they are close and the page still feels slow, open the browser's Network panel: the cause is images, scripts or a third-party tag (the waterfall).
  3. Which section? If the render is slow, work on a development environment, where every request renders. Time the page three times and take the middle value.
  4. Switch sections off one at a time with the switch below, timing each, and note which one saves the most.
  5. Read that section for a lookup or a relation inside a loop, or for deep component nesting (the three query-per-row shapes), fix it, and time again.
  6. Remove the switch before the change is released.

Code

Timing from the command line, three renders in a row:

Shell
for i in 1 2 3; do
  curl -s -o /dev/null -w '%{time_starttransfer}s\n' "https://dev.example.com/products/coffee?skip=$SKIP"
done

A switch around each expensive section of the page template, read from the URL, so that ?skip=related renders the page without that section and nothing has to be edited between timings:

Template code
{$skip = $_request->getParameter('skip')}
{if $skip != 'related'}{include 'products/includes/related-products.html'}{/if}
{if $skip != 'reviews'}{include 'products/includes/reviews.html'}{/if}
{if $skip != 'sections'}{$product->sections}{/if}
{% set skip = _request.getParameter('skip') %}
{% if skip != 'related' %}{% include 'products/includes/related-products.html' %}{% endif %}
{% if skip != 'reviews' %}{% include 'products/includes/reviews.html' %}{% endif %}
{% if skip != 'sections' %}{{ product.sections }}{% endif %}

Run the timing loop with SKIP empty, then set to each section name in turn. The section whose removal takes the most time off is the one to read.

Why this and not that

Measuring a development environment's absolute timing says little, because it never caches and recompiles every template; comparing two timings on it says a lot, which is why the switch compares the same page with and without one section. The switch reads a query parameter, which makes it part of the page's cache key, so it must not reach production: remove it in the same branch that fixes the problem. Elimination is the method that always works here: switch each section off in turn and time the difference.

Go deeper

Was this page helpful?

Your answer goes to the docs team.