# Make your Sylius webshop faster with Twig Runtime Extensions 🚀

## Intro
First of all, I got my inspiration from [this article](https://medium.com/@dotcom.software/reduce-symfony-response-time-with-lazy-twig-extensions-5b44647a4462), which was mentioned on [A Week of Symfony #719](https://symfony.com/blog/a-week-of-symfony-719-5-11-october-2020). As Margaret mentions, Sylius would be a good use case to add these improvements to. So said and done. 

## Setup
1. OS: Fedora 32
1. RAM: 12 gig DDR3
1. Database: MariaDB 10.4.11 - Docker Container
1. PHP Server: Symfony CLI - PHP 7.4.11
1. Default Sylius installation
1. APP_ENV=prod, APP_DEBUG=0

## The changes
For each bundle found in the `src\Sylius\Bundle`, I searched for Twig extensions. Only extensions that had dependencies I ported to a Runtime Extension. I made all the changes in one commit, so I would be able to easily go back and forth between the original installation and the adjusted code.

So this 

`ProvinceNamingExtension.php`

```php 
<?php 

...

class ProvinceNamingExtension extends AbstractExtension
{
    /** @var ProvinceNamingProviderInterface */
    private $provinceNamingProvider;

    public function __construct(ProvinceNamingProviderInterface $provinceNamingProvider)
    {
        $this->provinceNamingProvider = $provinceNamingProvider;
    }

    public function getFilters(): array
    {
        return [
            new TwigFilter('sylius_province_name', [$this->provinceNamingProvider, 'getName']),
            new TwigFilter('sylius_province_abbreviation', [$this->provinceNamingProvider, 'getAbbreviation']),
        ];
    }
}
```
Became this: 

`ProvinceNamingExtension.php`
```php 
<?php 

...

class ProvinceNamingExtension extends AbstractExtension
{
    public function getFilters(): array
    {
        return [
            new TwigFilter('sylius_province_name', [ProvinceNamingRuntimeExtension::class, 'getName']),
            new TwigFilter('sylius_province_abbreviation', [ProvinceNamingRuntimeExtension::class, 'getAbbreviation']),
        ];
    }
}
```
`ProvinceNamingRuntimeExtension.php`
```php
<?php

...

class ProvinceNamingRuntimeExtension implements RuntimeExtensionInterface
{
    /** @var ProvinceNamingProviderInterface */
    private $provinceNamingProvider;

    public function __construct(ProvinceNamingProviderInterface $provinceNamingProvider)
    {
        $this->provinceNamingProvider = $provinceNamingProvider;
    }

    public function getName($address)
    {
        return $this->provinceNamingProvider->getName($address);
    }

    public function getAbbreviation($address)
    {
        return $this->provinceNamingProvider->getAbbreviation($address);
    }
}
``` 

## Profiling

I used [Blackfire.io](https://blackfire.io) as a profiling tool. I ran the tests on `/en_US/taxons/category/dresses`. Before each profile, I manually removed the `var/cache` folder, ran `php bin/console cache:clear`, and gave the page one refresh. 

### The default installation profile:

![profile toolbar](https://dev-to-uploads.s3.amazonaws.com/i/hhb973mkj3ud7tt015kd.png)

[Detailed view](https://blackfire.io/profiles/c850f092-26f0-41b3-9607-d08a0989b03e/graph)

Some stats: 
1. Avg. Request time: 396 ms
1. Avg. IO Wait: 8.44 ms
1. Avg CPU Time: 361 ms
1. Avg. Peak Memory: 48.8MB


### Updated code profile:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/h7uc0gz7vazgji9hydal.png)

[Detailed view](https://blackfire.io/profiles/b2b25594-4fbd-4d19-bfdf-97f6ce09a4c6/graph)

Some stats:
1. Avg. Request time: 362 ms
1. Avg. IO Wait: 7.52 ms
1. Avg CPU Time: 355 ms
1. Avg. Peak Memory: 48.3MB

## Comparison:

Luckily for me, Blackfire itself supports making comparisons between two profiles. These are the results: 

[Detailed view](https://blackfire.io/profiles/compare/9e011fba-369b-4d85-8580-00f65e8c738f/graph)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/h7wisog50dzklsk80fzv.png)

1. Diff. Request time: -7ms (1.9%)
1. Diff. IO Wait: -922µs (11.%)
1. Diff CPU Time: -6.08 ms (1.68%)
1. Diff. Peak Memory: -84kb (0.174%)

## Conclusion
While the tone in the inspiring article 'suggested' these changes would make a big difference, I find the difference quite small. But It also depends on how much extensions are loaded for that page. Anyhow, each positive change is a good change. So I'd suggest, if you create a Twig extension that has dependencies, just create them as runtime extensions.
