En gang i mellem dukker der en samtale op, hvor folk er overbevist om, at kommentarer enten har eller ikke har indflydelse på ydeevnen.
Generelt vil jeg sige, at nej, kommentarer påvirker ikke ydeevnen , men der er altid plads til en "det afhænger af" ansvarsfraskrivelse. Lad os oprette en prøvedatabase og en tabel fuld af junk:
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
Nu vil jeg oprette fire lagrede procedurer - en med 20 tegn kommentarer, en med 2000, en med 20.000 og en med 200.000. Og det vil jeg gerne gøre igen, hvor kommentarerne er indlejret *indenfor* en forespørgselserklæring i proceduren, i modsætning til at være uafhængig (hvilket vil have en effekt på planens XML). Til sidst gentog jeg processen og tilføjede OPTION (RECOMPILE)
til forespørgslen.
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;
Nu skulle jeg generere koden for at køre hver procedure 100.000 gange, måle varigheden fra sys.dm_exec_procedure_stats
, og kontroller også størrelsen af planen i cachen.
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;
Lad os først se på størrelsen af procedureorganerne. Ingen overraskelser her, det bekræfter bare, at min byggekode ovenfor genererede den forventede størrelse af kommentarer i hver procedure:
Procedure | Størrelse (bytes) |
---|---|
Small_Separate / Small_Embedded | 378 |
Medium_Separate / Medium_Embedded | 4.340 |
Large_Separate / Large_Separate | 40.338 |
ExtraLarge_Separate / ExtraLarge_Separate | 400.348 |
Hvor store var dernæst planerne i cachen?
Procedure | Størrelse (bytes) |
---|---|
Small_Separate / Small_Embedded | 40.360 |
Medium_Separate / Medium_Embedded | 40.360 |
Large_Separate / Large_Separate | 40.360 |
ExtraLarge_Separate / ExtraLarge_Separate | 40.360 |
Til sidst, hvordan var forestillingen? Uden OPTION (RECOMPILE)
, her er den gennemsnitlige udførelsestid i millisekunder – ret konsistent på tværs af alle procedurer:
Gennemsnitlig varighed (millisekunder) – uden OPTION (RECOMPILE)
Med sætningsniveau OPTION (RECOMPILE)
, kan vi se omkring 50 % hit i gennemsnitlig varighed over hele linjen sammenlignet med ingen genkompilering, men stadig ret jævnt:
Gennemsnitlig varighed (millisekunder) – med OPTION (RECOMPILE)
I begge tilfælde, mens OPTION (RECOMPILE)
version kørte generelt langsommere, der var næsten NUL forskel i kørselstid, uanset kommentarstørrelse i procedureteksten.
Hvad med højere kompileringsomkostninger?
Dernæst ville jeg se, om disse store kommentarer ville have en enorm indvirkning på kompileringsomkostninger, for eksempel hvis procedurerne blev oprettet WITH RECOMPILE
. Byggekoden ovenfor var let at ændre for at tage højde for dette. Men i dette tilfælde kunne jeg ikke stole på sys.dm_exec_procedure_stats
, fordi dette ikke virker for procedurer WITH RECOMPILE
. Så min generationskode til testen var lidt anderledes, da jeg skulle spore den gennemsnitlige varighed manuelt:
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;
I dette tilfælde kunne jeg ikke kontrollere størrelsen af planerne i cachen, men jeg var i stand til at bestemme den gennemsnitlige kørselstid af procedurerne, og der var en forskel baseret på kommentarstørrelsen (eller måske bare procedurens kropsstørrelse):
Gennemsnitlig varighed (millisekunder) – MED REKOMPILERING på procedureniveau
Hvis vi sætter dem alle sammen på en graf, er det tydeligt, hvor meget dyrere WITH RECOMPILE
er. brug kan være:
Gennemsnitlig varighed (millisekunder) – sammenligning af alle tre metoder
Jeg vil nok se nærmere på dette på et senere tidspunkt for at se præcis, hvor den hockeystav kommer i spil – jeg forestiller mig at teste i trin på 10.000 tegn. For nu er jeg dog ret tilfreds med, at jeg har besvaret spørgsmålet.
Oversigt
Kommentarer ser ud til at være fuldstændig uden relation til faktisk, observerbar, lagret procedureydelse, undtagen i det tilfælde, hvor proceduren er defineret WITH RECOMPILE
. Personligt ser jeg ikke, at dette bliver brugt i naturen længere, men YMMV. For de subtile forskelle mellem denne mulighed og OPTION (RECOMPILE)
på sætningsniveau , se Paul Whites artikel, "Parameter Sniffing, Embedding, and the RECOMPILE Options."
Personligt tror jeg, at kommentarer kan være ekstremt værdifulde for alle, der skal gennemgå, vedligeholde eller fejlfinde din kode. Dette inkluderer fremtidige dig. Jeg anbefaler stærkt, at man ikke bekymrer sig om virkningen af ydeevnen af en rimelig mængde kommentarer, og i stedet fokuserer på at prioritere nytten af kontekst, som kommentarerne giver. Som en på Twitter sagde, er der en grænse. Hvis dine kommentarer svarer til den forkortede version af Krig og Fred, kan du overveje – med risiko for at afkoble koden fra dens dokumentation – at placere denne dokumentation et andet sted og henvise til linket i procedureorganets kommentarer.
For at minimere risikoen for afkobling, eller at dokumentationen og koden på anden måde bliver ude af sync med tiden, kan du oprette en anden procedure med suffikset _documentation
eller _comments
, og placere kommentarerne (eller en kommenteret version af koden) der. Sæt det måske i et andet skema for at holde det ude af hovedsorteringslisterne. I det mindste forbliver dokumentationen med databasen, uanset hvor den går, selvom den ikke garanterer, at den vil blive vedligeholdt. Det er uheldigt, at en normal procedure ikke kan oprettes WITH SCHEMABINDING
, i hvilket tilfælde du eksplicit kan knytte kommentarproceduren til kilden.