De temps en temps, une conversation surgit où les gens sont convaincus que les commentaires ont ou n'ont pas d'impact sur les performances.
En général, je dirai que non, les commentaires n'ont pas d'impact sur les performances , mais il y a toujours de la place pour une clause de non-responsabilité "ça dépend". Créons un exemple de base de données et une table remplie de fichiers inutiles :
CREATE DATABASE CommentTesting; GO USE CommentTesting; GO SELECT TOP (1000) n = NEWID(), * INTO dbo.SampleTable FROM sys.all_columns ORDER BY NEWID(); GO CREATE UNIQUE CLUSTERED INDEX x ON dbo.SampleTable(n); GO
Maintenant, je veux créer quatre procédures stockées - une avec 20 caractères de commentaires, une avec 2000, une avec 20 000 et une avec 200 000. Et je veux le refaire là où les commentaires sont intégrés * dans * une instruction de requête dans la procédure, au lieu d'être indépendants (ce qui aura un effet sur le plan XML). Enfin, j'ai répété le processus en ajoutant OPTION (RECOMPILE)
à la requête.
DECLARE @comments nvarchar(max) = N'', @basesql nvarchar(max), @sql nvarchar(max); SELECT TOP (5000) -- * 40 character strings @comments += N'--' + RTRIM(NEWID()) + CHAR(13) + CHAR(10) FROM sys.all_columns; SET @basesql = N'CREATE PROCEDURE dbo.$name$ AS BEGIN SET NOCOUNT ON; /* $comments1$ */ DECLARE @x int; SELECT @x = COUNT(*) /* $comments2$ */ FROM dbo.SampleTable OPTION (RECOMPILE); END'; SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Small_Separate'), N'$comments1$', LEFT(@comments, 20)); EXEC sys.sp_executesql @sql; SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Medium_Separate'), N'$comments1$', LEFT(@comments, 2000)); EXEC sys.sp_executesql @sql; SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Large_Separate'), N'$comments1$', LEFT(@comments, 20000)); EXEC sys.sp_executesql @sql; SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'ExtraLarge_Separate'), N'$comments1$', LEFT(@comments, 200000)); EXEC sys.sp_executesql @sql; SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Small_Embedded'), N'$comments2$', LEFT(@comments, 20)); EXEC sys.sp_executesql @sql; SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Medium_Embedded'), N'$comments2$', LEFT(@comments, 2000)); EXEC sys.sp_executesql @sql; SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Large_Embedded'), N'$comments2$', LEFT(@comments, 20000)); EXEC sys.sp_executesql @sql; SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'ExtraLarge_Embedded'), N'$comments2$', LEFT(@comments, 200000)); EXEC sys.sp_executesql @sql;
Maintenant, j'avais besoin de générer le code pour exécuter chaque procédure 100 000 fois, mesurer la durée à partir de sys.dm_exec_procedure_stats
, et vérifiez également la taille du plan en cache.
DECLARE @hammer nvarchar(max) = N''; SELECT @hammer += N' DBCC FREEPROCCACHE; DBCC DROPCLEANBUFFERS; GO EXEC dbo.' + [name] + N'; GO 100000 SELECT [size of ' + [name] + ' (b)] = DATALENGTH(definition) FROM sys.sql_modules WHERE [object_id] = ' + CONVERT(varchar(32),([object_id])) + N'; SELECT [size of ' + [name] + ' (b)] = size_in_bytes FROM sys.dm_exec_cached_plans AS p CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t WHERE t.objectid = ' + CONVERT(varchar(32),([object_id])) + N'; SELECT N''' + [name] + N''', avg_dur = total_elapsed_time*1.0/execution_count FROM sys.dm_exec_procedure_stats WHERE [object_id] = ' + CONVERT(varchar(32),([object_id])) + N';' FROM sys.procedures WHERE [name] LIKE N'%[_]Separate' OR [name] LIKE N'%[_]Embedded'; PRINT @hammer;
Voyons d'abord la taille des organes de procédure. Pas de surprises ici, confirmant simplement que mon code de construction ci-dessus a généré la taille attendue des commentaires dans chaque procédure :
Procédure | Taille (octets) |
---|---|
Small_Separate / Small_Embedded | 378 |
Medium_Separate / Medium_Embedded | 4 340 |
Large_Separate / Large_Separate | 40 338 |
ExtraLarge_Separate / ExtraLarge_Separate | 400 348 |
Ensuite, quelle était la taille des plans dans le cache ?
Procédure | Taille (octets) |
---|---|
Small_Separate / Small_Embedded | 40 360 |
Medium_Separate / Medium_Embedded | 40 360 |
Large_Separate / Large_Separate | 40 360 |
ExtraLarge_Separate / ExtraLarge_Separate | 40 360 |
Enfin, à quoi ressemblait la performance ? Sans OPTION (RECOMPILE)
, voici le temps d'exécution moyen, en millisecondes - assez cohérent pour toutes les procédures :
Durée moyenne (millisecondes) – sans OPTION (RECOMPILE)
Avec OPTION (RECOMPILE)
au niveau de l'instruction , nous pouvons constater un impact d'environ 50 % de la durée moyenne dans tous les domaines par rapport à l'absence de recompilation, mais toujours assez homogène :
Durée moyenne (millisecondes) – avec OPTION (RECOMPILE)
Dans les deux cas, alors que OPTION (RECOMPILE)
la version fonctionnait généralement plus lentement, il n'y avait pratiquement ZERO différence d'exécution, quelle que soit la taille du commentaire dans le corps de la procédure.
Qu'en est-il des coûts de compilation plus élevés ?
Ensuite, je voulais voir si ces gros commentaires auraient un impact énorme sur les coûts de compilation, par exemple si les procédures étaient créées WITH RECOMPILE
. Le code de construction ci-dessus était facile à modifier pour tenir compte de cela. Mais dans ce cas, je ne pouvais pas compter sur sys.dm_exec_procedure_stats
, car cela ne fonctionne pas pour les procédures WITH RECOMPILE
. Mon code de génération pour le test était donc un peu différent, car je devais suivre manuellement la durée moyenne :
DECLARE @hammer nvarchar(max) = N''; SELECT @hammer += N' DBCC FREEPROCCACHE; DBCC DROPCLEANBUFFERS; SELECT SYSDATETIME(); GO EXEC dbo.' + [name] + N'; GO 100000 SELECT SYSDATETIME();'; PRINT @hammer;
Dans ce cas, je n'ai pas pu vérifier la taille des plans dans le cache, mais j'ai pu déterminer le temps d'exécution moyen des procédures, et il y avait une différence en fonction de la taille des commentaires (ou, peut-être, simplement de la taille du corps de la procédure) :
Durée moyenne (millisecondes) – AVEC RECOMPILE au niveau de la procédure
Si nous les mettons tous ensemble sur un graphique, il est clair combien plus cher le WITH RECOMPILE
l'utilisation peut être :
Durée moyenne (millisecondes) – comparaison des trois méthodes
J'examinerai probablement cela de plus près plus tard pour voir exactement où ce bâton de hockey entre en jeu - j'envisage des tests par incréments de 10 000 caractères. Pour l'instant, cependant, je suis assez satisfait d'avoir répondu à la question.
Résumé
Les commentaires semblent n'avoir aucun rapport avec les performances réelles et observables de la procédure stockée, sauf dans le cas où la procédure est définie WITH RECOMPILE
. Personnellement, je ne vois plus cela être utilisé dans la nature, mais YMMV. Pour les différences subtiles entre cette option et OPTION (RECOMPILE)
au niveau de l'instruction , consultez l'article de Paul White, "Parameter Sniffing, Embedding, and the RECOMPILE Options."
Personnellement, je pense que les commentaires peuvent être extrêmement précieux pour quiconque doit réviser, maintenir ou dépanner votre code. Cela inclut le futur vous. Je déconseille fortement de s'inquiéter de l'impact sur les performances d'un nombre raisonnable de commentaires, et de se concentrer plutôt sur la priorisation de l'utilité du contexte fourni par les commentaires. Comme l'a dit quelqu'un sur Twitter, il y a une limite. Si vos commentaires correspondent à la version abrégée de Guerre et Paix, vous pourriez envisager - au risque de dissocier le code de sa documentation - de mettre cette documentation ailleurs et de référencer le lien dans les commentaires du corps de la procédure.
Pour minimiser le risque de découplage ou de désynchronisation de la documentation et du code au fil du temps, vous pouvez créer une deuxième procédure, avec le suffixe _documentation
ou _comments
, et y mettre les commentaires (ou une version commentée du code). Peut-être le mettre dans un schéma différent pour le garder hors des listes de tri principales. Au moins, la documentation reste avec la base de données où qu'elle aille, même si cela ne garantit pas qu'elle sera maintenue. Il est dommage qu'une procédure normale ne puisse pas être créée WITH SCHEMABINDING
, auquel cas vous pouvez lier explicitement la procédure de commentaire à la source.