Twig Templates, Initial Cache Hit

OroCRM uses Symfony's delegate template engine, which means developers are free to use any template service added to the Symfony configuration. This usually means twig.

I'm tracing the execution of a full twig render, and I'm starting to see why twig's initial cache hit is so performance heavy.

Here's one example — the isTemplateFresh method is where some of the "do I need to recompile the twig templates" logic lives. In plain english, this method checks if any of the configured twig extension classes have changed, and then checks if the specific twig template has changed. If so, it signals to the system that a recompile is needed.

This is a valuable developer convenience, and means we're not recompiling templates after making small changes. (What are we — C programmers?)

In php code, it looks like this

#File: vendor/twig/twig/lib/Twig/Environment.php
public function isTemplateFresh($name, $time)
{
    foreach ($this->extensions as $extension) {
        $r = new ReflectionObject($extension);
        if (filemtime($r->getFileName()) > $time) {
            return false;
        }
    }

    return $this->getLoader()->isFresh($name, $time);
}

This method loops over each configured twig extension and

  • Creates a reflection object to grab the file name
  • Then stats the extension file to get its last modified date
  • Assuming the extensions haven't changed, it stats the specific twig template with isFresh

That's a lot of work — and this will happen for each twig template in the layout. If you have 5 twig extensions and a simple layout with 3 templates, that's 15 times through the loop. Add a new template, that's 20 times through the loop. This is the sort of thing that would crush a production server under heavy load.

Of course, the mantra of premature optimization is the root of all evil is on your lips. Hop up to where isTemplateFresh gets called.

if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) {
    $this->writeCacheFile($cache, $this->compileSource($this->getLoader()->getSource($name), $name));
}

and you'll see it's easily short circuited if isAutoReload returns false, which is how a production system using twig should be configured.

Ever after all these years of programming, my instinctual mind wants nothing to do with this sort of code. However, like anyone who has a long career in programming, learning to accept this is how the bowels of systems look is step one to putting yourself in a position to make things better.