Le blog de Noël GUILBERT

Aller au contenu Aller au menu

jeudi, avril 22 2010

Doctrine : DQL Callbacks et performances

La documentation de Doctrine indique que l'utilisation des DQL Callbacks a un impact sur les performances:

The DQL callbacks are off by default due to them costing a little extra.

Et aujourd'hui, un client m'a bien évidemment dit :

Oui, mais combien ?

j'ai donc fait quelques métriques sur un projet symfony afin d'étudier l'impact des "DQL Callbacks" sur les performances de l'application.

La méthode est la suivante :

  1. Effectuer les benchmark suivant : DQL callbacks désactivés, DQL Callbacks activés, DQL Callback activés + DQL Callback activé + Cache de requêtes activé
  2. Dans un 1er temps, effectuer des benchmarks sur une base de donnée vide, afin d'avoir l'impact de cette fonctionnalité sur l'exécution de la requêtes : on évite ainsi le processus d'hydratation.
  3. Dans un second temps, effectuer la même batterie de tests sur une base de donnée contenant une vingtaine d'enregistrement, afin de se rapprocher d'un cas concret.

Voici le code qui m'a permis de réaliser les benchmarks :

$time = microtime(true);
for ($i = 0; $i < 1000; $i++)
{
  Doctrine_Core::getTable('Bookmarks')->findAll();
}

echo microtime(true) - $time, "\n";

C'est relativement simple, il s'agit de lancer 1000 fois la même requête.

Les premiers tests, avec la base vide, m'ont donnés les résultats suivants

RAW  | DQL CALLBACKS | DQL+Cache
--------------------------------
1000 | 1000          | 1000
2,5s | 4s            | 2,7s

Comme vous le constatez, le surcoût est de quasiment 50%, ce qui est loin d'être négligeable. Cependant, une fois le cache de requêtes activé, c'est plutôt raisonnable.

Les seconds tests m'ont donné les résultats suivants

RAW  | DQL CALLBACKS | DQL+Cache
--------------------------------
1000 | 1000          | 1000
9,3s | 10,9s         | 9,3s

Dans cette configuration, l'impact est nettement moindre, de l'ordre de 15%. Cela est du au processus d'hydratation qui est particulièrement coûteux en temps. Et encore une fois, dès que l'on active le cache de requêtes, le performances sont meilleures, et même identiques dans mon cas.

Conclusion

Utilisez le cache de requêtes si vous voulez utiliser les DQL Callbacks.

Note: Tests effectués avec Doctrine 1.2 et PHP 5.3

dimanche, février 21 2010

Utiliser Doctrine 2 avec Symfony

Pour ceux qui souhaitent tester Symfony 2, il faut savoir que Doctrine 2 est également fournit avec le framework, et déjà pré-configuré. Il n'y a donc presque rien à faire pour que ça fonctionne.

Activer l'ORM

Dans le fichier config.yml de votre projet, ajoutez la configuration suivante:

doctrine.orm: ~

doctrine.dbal:
  dbname:    blogdb
  username: bloguser
  password:  s3cr3t

Charger les entités

Pour charger les entités, il faut utiliser le ClassLoader de Doctrine, et celui-ci à besoin de savoir où sont stockés les entités :

# /path/to/your/Bundle/Bundle.php

<?php

namespace Bundle\BlogBundle;

use Symfony\Foundation\Bundle\Bundle as BaseBundle;
use Symfony\Components\DependencyInjection\ContainerInterface;
use Doctrine\Common\ClassLoader;

class Bundle extends BaseBundle
{

  public function boot(ContainerInterface $container)
  {
    $entitiesClassLoader = new ClassLoader('Entities', __DIR__.'/model/Entities');
    $entitiesClassLoader->register();
  }
}

Créez un répertoire Entities dans le bundle, et ajoutez-y quelques entités:

<?php

namespace Bundle\BlogBundle\Model\Entities;

/**
 * @Entity
 * @Table(name="blog_post")
 */

class BlogPost {
    /**
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */

    private $id;
    /** @Column(type="string", length=255) */
    private $title;

    /** @Column(type="string", length=255) */
    private $slug;
   
    /** @Column(type="string") */
    private $body;
}

Ensuite, utiliser le conteneur de service pour récupérer l'EntityManager et manipuler vos objets:

class BlogController extends Controller
{
  public function showAction($slug)
  {
    $em   = $this->container->getService('doctrine.orm.manager');
    $post = $em->getRepository('Bundle\BlogBundle\Model\Entities\BlogPost')->findOneBy(array('slug' => $slug));

    return $this->render('BlogBundle:Blog:show', array('post' => $post));
  }
}