Entity Framework - Première requête lente

.net entity-framework performance

Question

Comme le titre le suggère, la première requête pose un problème avec une base de données SQL Server utilisant Entity Framework.

J'ai essayé de chercher une réponse sur différents sites mais personne ne semble réellement avoir de solution à ce problème.

Je charge pas mal de lignes de la base de données, y compris deux relations 0-plusieurs.

Les tests ont été réalisés dans Visual Studio 2010 à l'aide du modèle Entity Framework 4.0 et du générateur POCO (il n'y a pas beaucoup de différences de minutage entre les entités normales et les objets POCO). J'ai également utilisé le modèle de vues T4 pour pré-compiler les vues.

La base de données était sur SQL Server 2008.

Ce que j'aimerais vraiment savoir, c'est pourquoi la première requête est tellement plus lente que les requêtes secondaires.

Je veux également savoir si quelque chose peut être fait pour augmenter la vitesse de la première requête à un point où elle est dans des limites acceptables.

Il s’agit là d’une requête volumineuse. Nous pourrions en avoir d’autres encore plus grandes et il est compréhensible qu’elles soient un peu lentes, mais 30 secondes sont beaucoup trop lentes pour l’attente de l’utilisateur, en particulier lorsque les ensembles de données peuvent obtenir les mêmes données beaucoup plus rapidement.

J'ai fait quelques tests de synchronisation pour essayer de trouver où se situe le problème et j'ai été un peu surpris de voir qu'il semble que ce soit le serveur SQL qui soit lent sur la première requête.

Les horaires étaient les suivants:

Application de test .NET:

  • Première requête: 29,6 secondes
  • Deuxième requête: 3,2 secondes

Profiler SQL:

  • Première requête: 27 secondes
  • Deuxième requête: 3,2 secondes

Fenêtre de requête SQL Server

  • Première requête: 8 secondes
  • Deuxième requête: 4 secondes

Les temps dans l'application ont été mesurés avec la classe Stopwatch . Seule la requête a été mesurée et .ToList() été utilisé pour exécuter la requête.

Les minutages dans SQL Server Profiler concernent les mêmes requêtes que celles exécutées dans l'application, ce qui montre que l'application ne prend que 2,6 secondes environ pour remplir les données dans les objets.

Les 27 dernières secondes sont utilisées pour exécuter la requête sur SQL Server.

En regardant la requête secondaire, les timings sont les mêmes pour l'application et le serveur SQL, mais l'exécution de la requête est beaucoup plus rapide cette fois.

Je peux comprendre pourquoi l’application n’utilise pas à tout moment parce qu’aucune nouvelle ligne n’a besoin d’être convertie en objet, mais pourquoi la requête est-elle tellement plus rapide, je me serais attendu à quelques secondes à cause des plans d’exécution mais pas de 24 secondes.

Juste à des fins de test, j'ai copié le code SQL généré par Entity Framework, ouvert une nouvelle fenêtre de requête avec une connexion distincte et exécuté la requête.

Comme vous pouvez le constater, cela prend 8 secondes pour la première requête et 4 secondes pour la seconde.

J'espère que quelqu'un a des suggestions.

ps. Je m'excuse pour le mur de texte :)

Edit 19-10-2010:
J'ai fait un test hier qui semble confirmer que les lignes sont renvoyées de manière séquentielle. Cela signifie que lorsqu'une ligne est renvoyée de la base de données, elle est immédiatement matérialisée (si elle n'existe pas déjà dans le contexte), la ligne suivante est renvoyée, etc.

C'est pourquoi il semble que la requête prenne beaucoup de temps sur le serveur de base de données car le temps de matérialisation est inclus dans les timings de l'éditeur de profil SQL Server.

Je ne crois pas que ce soit un cas de lecture de SQL Server à partir du disque dur. La requête lente se produit chaque fois qu'il y a une "première requête" dans EF.

ex.

  1. Exécuter la première requête avec EF, l'instruction SQL est plus lente que toute requête secondaire
  2. Éliminer le contexte / référentiel
  3. Créer un nouveau contexte
  4. Exécuter la même requête que précédemment (encore une fois, la première requête est lente, de même que l'instruction SQL)

C'est presque comme si EF envoyait des options avec la première requête qui ralentissait le serveur.

En ce qui concerne la compilation de requêtes, je me souviens que la requête est compilée dès la première utilisation, ce qui signifie que la première requête prendrait encore plus de temps à s'exécuter.

Les requêtes secondaires seraient plus rapides, mais la vitesse sur les requêtes secondaires n'est pas le problème.

J'ai également fait un test où j'ai créé une requête compilée en tant que statique afin qu'elle soit compilée pour tous les contextes créés.

J'ai ensuite créé un contexte, exécuté la requête, détruit le contexte, créé un nouveau et exécuté à nouveau la même requête.

La différence n'était pas si grande, seulement quelques secondes et la toute première fois que j'ai exécuté la requête, cela prenait toujours aussi longtemps sans la pré-compiler.

En ce qui concerne la génération de vues, nous l’implémentons déjà à l’aide de modèles T4.

La réponse est-elle vraiment que EF ne fonctionne que si vous ne faites rien d'autre que les requêtes les plus simples qui ne renvoient qu'une quantité relativement petite de données?

Réponse populaire

Nous avions le même problème dans EF 5.0 et, à ce jour, une recherche superficielle dans Google ne révèle pas une vitesse suffisante.

Selon ce lien, http://msdn.microsoft.com/en-us/library/cc853327(v=vs.100).aspx "Chargement des métadonnées" entraîne un coût de temps modéré mais ne doit être généré qu'une fois par AppDomain. Je n'ai trouvé aucune pré-compilation comme astuces pour charger les méta-données.

La solution de contournement que nous avons implémentée consiste à effectuer une requête mineure sur le contexte dans un thread séparé lors du lancement de l'application. Cela charge les méta-données, cela prend toujours beaucoup de temps (18 à 19 secondes dans notre cas), mais l'application réagit pendant le chargement. De plus, la première charge réelle ne prend pas aussi longtemps.

Veuillez noter que dans notre contexte, il est possible pour l'utilisateur de passer 18 à 19 secondes dans l'application avant qu'un appel EF ne soit nécessaire en réponse à ses actions. Évidemment, si cela n’est pas possible dans votre application, cette solution de contournement risque de ne pas augmenter considérablement la vitesse.



Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow