Retour au blog

Benchmark de méthodes d'inclusion d'un fragment de template avec Twig et Symfony

Durant le développement d'un site internet, nous nous retrouvons régulièrement confrontés au besoin d'inclure un fragment de template complexe dans plusieurs pages. Un bon exemple serait un formulaire de newsletter ou un menu. Mais y a-t-il une méthode objectivement optimale pour procéder ?

Lors d'un hackathon organisé chez Novaway, j'ai décidé de m'attaquer à cette question. J'ai donc appliqué une méthode scientifique dans le but d'apporter des éléments de réponse circonstanciés à cette problématique récurrente.

Protocole de test

Pour chaque méthode, le but va être d'afficher une page avec un certain nombre de formulaires très simples. Les formulaires ont été choisis car simples à implémenter, mais nécessitant un temps de calcul du coté Symfony (pas juste un écho).
Les tests ont été réalisés sur Billon Immobilier, un projet existant (donc avec tous les chargements de configuration).

Méthodes testées

nocache
Tous les formulaires sont directement créés dans le Controller principal.

with_render
Tous les formulaires sont créés chacun par un appel à {{ render(controller) }} de twig.

with_twig_extension
Tous les formulaires sont créés chacun par un appel à une Twig_extension, qui crée et rend le formulaire grâce au combo FormFactory \ TwigEngine.

with_esi
Tous les formulaires sont créés chacun par un appel à {{ render(controller) }} de twig dans un ESI .
Le reverse proxy utilisé est celui de Symfony par soucis de simplicité

Variables d'ajustement

On étudiera les comportements en modifiant ces variables :

  • Le nombre de formulaires par page
  • Le nombre de fois où la page est appelée
  • Si les valeurs des forms sont modifiées ou non (si tous les forms sont indépendants)
  • L'étendue de la plage de valeurs de form

Jeux de données

Beaucoup d'éléments par page, une seule fois

Parameters
----------

    --------------- ----------------- ------------
    Elt per page    Number of tests   Random max
    --------------- ----------------- ------------
    5000            1                 10
    --------------- ----------------- ------------

First simulation : no randomization
-----------------------------------

    --------------------- ----------
    Type                  Duration
    --------------------- ----------
    nocache               4383.6
    with_render           11760.2
    with_twig_extension   4921.4
    with_esi              5586.4
    --------------------- ----------

Second simulation : randomization
---------------------------------

    --------------------- ----------
    Type                  Duration
    --------------------- ----------
    nocache               4355.6
    with_render           10431.2
    with_twig_extension   4739.9
    with_esi              5478.1
    --------------------- ----------

Classement :

  • nocache
  • with_twig_extension (+11%)
  • with_esi  (+26%)
  • with_render (+153%)

Conclusion : la méthode with_render est loin derrière. Les deux méthodes sans SubRequests sont les plus performantes, avec la solution des TwigExtensions un peu derrière.
La méthode avec les ESI est 10 à 20% moins performante, mais reste acceptable.

Nombre moyen d'éléments, un nombre moyen d'accès

Parameters
----------

    --------------- ----------------- ------------
    Elt per page    Number of tests   Random max
    --------------- ----------------- ------------
    100             10                10
    --------------- ----------------- ------------

First simulation : no randomization
-----------------------------------

    --------------------- ----------
    Type                  Duration
    --------------------- ----------
    nocache               2506.3    
    with_render           11423.4   
    with_twig_extension   7215.2    
    with_esi              2657
    --------------------- ----------

Second simulation : randomization
---------------------------------

    --------------------- ----------
    Type                  Duration
    --------------------- ----------
    nocache               2655.9    
    with_render           13129.8   
    with_twig_extension   7054.7    
    with_esi              2680.9
    --------------------- ----------

Classement :

  • nocache
  • with_esi (+3%)
  • with_twig_extension (+176%)
  • with_render (+376%)

Conclusion : la méthode `with_render` est toujours loin derrière. La méthode `nocache` est toujours devant, mais pas significativement devant les ESI. Cette fois les `with_twig_extension` sont bien derrière les deux autres méthodes compétitives.

Un seul element, beaucoup de fois

Parameters
-----------

    --------------- ----------------- ------------
    Elt per page    Number of tests   Random max
    --------------- ----------------- ------------
    1               1000              10
    --------------- ----------------- ------------

First simulation : no randomization
-----------------------------------

    --------------------- ----------
    Type                  Duration
    --------------------- ----------
    nocache               178118    
    with_render           184976.4  
    with_twig_extension   184623.2  
    with_esi              161696.8
    --------------------- ----------

Second simulation : randomization
---------------------------------
    
    --------------------- ----------
    Type                  Duration
    --------------------- ----------
    nocache               172071.7  
    with_render           176265.8  
    with_twig_extension   179850.4  
    with_esi              157780.5
    --------------------- ---------- 

Cette mesure est la plus intéressante, pouvant représenter un élément de footer par exemple.

Classement :

  • with_esi
  • nocache (+10%)
  • with_render  (+13%)
  • with_twig_extension (+14%)

Conclusion : cette fois, la méthode des ESI est environ 15% plus performante que les autres, qui sont beaucoup plus proches. La différence entre le `with_render` et le `with_twig_extension` est bien plus ténue, le rapport s'inversant même dans le cas de la randomization : cela peut ceci dit être un artefact.

Plage de random >> nombre de tests

La plage de randomization étant bien plus importante que le nombre de tests, on peut s'attendre à ce que rien ne soit caché.

Parameters
----------

    --------------- ----------------- ------------
    Elt per page    Number of tests   Random max
    --------------- ----------------- ------------
    1               100               1000000
    --------------- ----------------- ------------

First simulation : no randomization
-----------------------------------
    
    --------------------- ----------
    Type                  Duration
    --------------------- ----------
    nocache               17572.1   
    with_render           17771.8   
    with_twig_extension   18026.1   
    with_esi              15987.7
    --------------------- ----------

Second simulation : randomization
---------------------------------
    
    --------------------- ----------
    Type                  Duration
    --------------------- ----------
    nocache               16740     
    with_render           17595     
    with_twig_extension   17594.4   
    with_esi              19878.7
    --------------------- ----------

Classement :

  • nocache
  • with_twig_extension (+5%)
  • with_render  (+5%)
  • with_esi (+18%)

Conclusion : comme attendu, dans cette configuration sans randomization les ESI arrivent avant. Avec randomization, ils sont assez loin derrière (même si on n'a pas les résultats catastrophiques de with_render dans les premiers tests).

Conclusion

Il est à noter que le nombre de mesures n'est pas suffisant pour pouvoir statuer sur les conclusions. Cependant, les quelques résultats testés semblent tous indiquer les mêmes tendances.

Toutes les alternatives à "générer le code dans la méthode principale du Controller" ont un coût (test1). Par contre, dès qu'il est possible de cacher ces fragments, le coût de la méthode with_esi diminue, devenant même inférieur à la méthode nocache quand le nombre de requêtes devient grand (test2 et test3).

On voit également que plus le nombre de tests augmente et le nombre d'éléments par page diminue, plus les méthodes with_render et with_twig_extension ont des performances proches.

De plus, il est important de considérer que le fragment utilisé pour le test est très léger pour le processeur. Ces écarts devraient normalement s'accentuer si le traitement nécessaire devient plus lourd.

Par ailleurs, on est ici dans le cas d'un reverse cache proxy peu performant (celui de Symfony). On peut imaginer obtenir des performances optimisées avec une meilleure solution. Néanmoins, si les fragments sont très différents (et donc non cacheable), les performances des ESI deviennent bien pires que les autres méthodes.