Performances de l’architecture d’intégration clR
S’applique à : SQL Server Azure SQL Managed Instance
Cet article décrit certains des choix de conception qui améliorent les performances de l’intégration de SQL Server au Common Language Runtime (CLR) de Microsoft .NET Framework.
Processus de compilation
Lors de la compilation d’expressions SQL, lorsqu’une référence à une routine managée est rencontrée, un stub MSIL (Microsoft Intermediate Language) est généré. Ce stub inclut du code pour marshaler les paramètres de routine de SQL Server vers le CLR, appeler la fonction et retourner le résultat. Ce code de type glue est basé sur le type de paramètre et sur la direction du paramètre (in, out ou reference).
Le code de collage permet des optimisations spécifiques au type et garantit une application efficace de la sémantique SQL Server, telle que la possibilité de nullabilité, la contrainte des facettes, la gestion des exceptions par valeur et standard. En générant le code pour les types exacts des arguments, vous évitez le conflit de types ou les coûts de création d'objet de wrapper (ou « conversion boxing ») à travers la limite d'appel.
Le stub généré est ensuite compilé en code natif et optimisé pour l’architecture matérielle particulière sur laquelle SQL Server s’exécute, à l’aide des services de compilation juste-à-temps (JIT) du CLR. Les services JIT sont appelés au niveau de la méthode et permettent à l’environnement d’hébergement SQL Server de créer une unité de compilation unique qui s’étend à la fois sur SQL Server et l’exécution du CLR. Une fois le stub compilé, le pointeur fonction résultant devient l'implémentation à l'exécution de la fonction. Cette approche de génération de code garantit qu’aucun coût d’appel supplémentaire n’est lié à la réflexion ou à l’accès aux métadonnées au moment de l’exécution.
Transitions rapides entre SQL Server et CLR
Le processus de compilation génère une fonction pointeur qui peut être appelée au moment de l'exécution à partir du code natif. Pour les fonctions scalaires définies par l’utilisateur, cet appel de fonction se produit par ligne. Pour réduire le coût de la transition entre SQL Server et le CLR, les instructions qui contiennent n’importe quel appel managé ont une étape de démarrage pour identifier le domaine d’application cible. Cette étape d'identification réduit le coût de la transition pour chaque ligne.
Considérations relatives aux performances
La section suivante récapitule les considérations relatives aux performances spécifiques à l’intégration clR dans SQL Server. Pour plus d’informations, consultez Utilisation de l’intégration CLR dans SQL Server 2005. Pour plus d’informations sur les performances du code managé, consultez Amélioration des performances et de l’extensibilité des applications .NET.
Fonctions définies par l’utilisateur
Les fonctions CLR bénéficient d’un chemin d’appel plus rapide que les fonctions transact-SQL définies par l’utilisateur. En outre, le code managé présente un avantage décisif sur Transact-SQL en termes de code, de calcul et de manipulation de chaînes. Les fonctions CLR nécessitant beaucoup d’informations et qui n’effectuent pas d’accès aux données sont mieux écrites dans le code managé. Toutefois, les fonctions Transact-SQL effectuent un accès aux données plus efficacement que l’intégration CLR.
Fonctions d'agrégation définies par l'utilisateur
Le code managé se révèle considérablement plus performant que l'agrégation à base de curseur. Le code managé effectue généralement un peu plus lent que les fonctions d’agrégation SQL Server intégrées. S'il existe une fonction d'agrégation intégrée native, il est recommandé de l'utiliser. Dans les cas où l’agrégation nécessaire n’est pas prise en charge en mode natif, considérez un agrégat CLR défini par l’utilisateur sur une implémentation basée sur un curseur pour des raisons de performances.
Fonctions table de diffusion en continu
Les applications doivent souvent retourner une table comme résultat de l'appel d'une fonction. Les exemples incluent la lecture de données tabulaires à partir d'un fichier dans le cadre d'une opération d'importation et la conversion de valeurs séparées par des virgules dans le cas d'une représentation relationnelle. En général, vous pouvez accomplir ces actions en matérialisant la table de résultats et en la remplissant avant qu'elle ne puisse être consommée par l'appelant. L’intégration du CLR à SQL Server introduit un nouveau mécanisme d’extensibilité appelé fonction table de diffusion en continu (STVF). Les fonctions table en continu offrent de meilleures performances que les implémentations de procédure stockée étendue comparables.
Les fonctions table en continu sont des fonctions managées qui retournent une interface IEnumerable
. IEnumerable
possède des méthodes pour se déplacer à travers le jeu de résultats retourné par la fonction table en continu. Lorsque la fonction table en continu est appelée, l'interface IEnumerable
retournée est directement connectée au plan de requête. Le plan de requête appelle les méthodes IEnumerable
lorsqu'il doit extraire des lignes. Ce modèle d'itération permet que les résultats soient consommés immédiatement après que la première ligne a été créée, au lieu d'attendre que la totalité de la table soit remplie. Il réduit aussi considérablement la mémoire consommée en appelant la fonction.
Tableaux et curseurs
Lorsque les curseurs Transact-SQL doivent parcourir les données plus facilement exprimées sous forme de tableau, le code managé peut être utilisé avec des gains de performances significatifs.
Données de chaîne
Les données de caractères SQL Server, telles que varchar, peuvent être de type SqlString ou SqlChars dans les fonctions managées. Les variables SqlString créent une instance de la valeur entière en mémoire. Les variables SqlChars fournissent une interface multidiffusion qui peut être utilisée pour obtenir de meilleures performances et une meilleure évolutivité en ne créant pas d'instance de la totalité de la valeur en mémoire. Cela devient important pour les données d’objet volumineux (LOB). En outre, il est possible d'accéder aux données XML du serveur via une interface de diffusion retournée par SqlXml.CreateReader()
.
CLR et procédures stockées étendues
Les Microsoft.SqlServer.Server
interfaces de programmation d’applications (API) qui permettent aux procédures managées d’envoyer des jeux de résultats au client fonctionnent mieux que les API Open Data Services (ODS) utilisées par des procédures stockées étendues. En outre, les API System.Data.SqlServer prennent en charge les types de données tels que xml, varchar(max), nvarchar(max)et varbinary(max), introduits dans SQL Server 2005 (9.x), tandis que les API ODS n’ont pas été étendues pour prendre en charge les nouveaux types de données.
Avec le code managé, SQL Server gère l’utilisation de ressources telles que la mémoire, les threads et la synchronisation. Cela est dû au fait que les API managées qui exposent ces ressources sont implémentées sur le gestionnaire de ressources SQL Server. À l’inverse, SQL Server n’a pas de vue ou de contrôle sur l’utilisation des ressources de la procédure stockée étendue. Par exemple, si une procédure stockée étendue consomme trop de ressources processeur ou mémoire, il n’existe aucun moyen de détecter ou de contrôler cela avec SQL Server. Toutefois, avec le code managé, SQL Server peut détecter qu’un thread donné n’a pas été généré pendant une longue période, puis forcer la tâche à générer afin que d’autres travaux puissent être planifiés. Par conséquent, l’utilisation du code managé offre une meilleure scalabilité et une meilleure utilisation des ressources système.
Le code managé peut entraîner une surcharge supplémentaire nécessaire pour maintenir l’environnement d’exécution et effectuer des vérifications de sécurité. C’est le cas, par exemple, lors de l’exécution à l’intérieur de SQL Server et de nombreuses transitions entre le code managé et le code natif sont requises (car SQL Server doit effectuer une maintenance supplémentaire sur les paramètres spécifiques au thread lors du passage au code natif et au retour). Par conséquent, les procédures stockées étendues peuvent considérablement surperformer le code managé s’exécutant à l’intérieur de SQL Server pour les cas où il existe des transitions fréquentes entre le code managé et natif.
Remarque
Ne développez pas de nouvelles procédures stockées étendues, car cette fonctionnalité est déconseillée.
Sérialisation native pour les types définis par l’utilisateur
Les types définis par l'utilisateur (UDT) sont conçus comme mécanisme d'extensibilité du système de types scalaires. SQL Server implémente un format de sérialisation pour les UDT appelés Format.Native
. Pendant la compilation, la structure du type est examinée pour générer un langage MSIL personnalisé pour cette définition de type particulière.
La sérialisation native est l’implémentation par défaut pour SQL Server. La sérialisation définie par l'utilisateur appelle une méthode définie par le créateur du type pour effectuer la sérialisation. La sérialisation Format.Native
doit être utilisée si possible pour de meilleures performances.
Normalisation des UDT comparables
Les opérations relationnelles, telles que le tri et la comparaison d'UDT, fonctionnent directement sur la représentation binaire de la valeur. Cette tâche s'effectue en stockant une représentation normalisée (classement binaire) de l'état de l'UDT sur le disque.
La normalisation présente deux avantages :
il rend l’opération de comparaison considérablement moins coûteuse en évitant la construction de l’instance de type et la surcharge d’appel de méthode
il crée un domaine binaire pour l’UDT, ce qui permet la construction d’histogrammes, d’index et d’histogrammes pour les valeurs du type.
Par conséquent, les UDT normalisés ont un profil de performances similaire aux types intégrés natifs pour les opérations qui n’impliquent pas d’appel de méthode.
Utilisation évolutive de la mémoire
Pour que le garbage collection managé fonctionne correctement dans SQL Server, évitez une allocation unique volumineuse. Les allocations supérieures à 88 kilo-octets (Ko) sont placées sur le tas d’objets volumineux, ce qui entraîne l’exécution et la mise à l’échelle d’un garbage collection pire que de nombreuses allocations plus petites. Par exemple, si vous avez besoin d’allouer un tableau multidimensionnel volumineux, il est préférable d’allouer un tableau délimité (éparpillé).