Jednou za čas se objeví konverzace, kde jsou lidé přesvědčeni, že komentáře buď mají, nebo nemají vliv na výkon.
Obecně řeknu, že ne, komentáře nemají vliv na výkon , ale vždy existuje prostor pro odmítnutí odpovědnosti. Vytvořme vzorovou databázi a tabulku plnou harampádí:
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
Nyní chci vytvořit čtyři uložené procedury – jednu s 20 znaky komentářů, jednu s 2000, jednu s 20 000 a jednu s 200 000. A chci to udělat znovu tam, kde jsou komentáře vložené *uvnitř* příkazu dotazu v rámci procedury, na rozdíl od nezávislosti (což bude mít vliv na plán XML). Nakonec jsem zopakoval proces přidáním OPTION (RECOMPILE)
na dotaz.
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;
Nyní jsem potřeboval vygenerovat kód pro spuštění každé procedury 100 000krát, změřit dobu trvání z sys.dm_exec_procedure_stats
a také zkontrolujte velikost plánu v mezipaměti.
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;
Nejprve se podívejme na velikost orgánů procedury. Žádné překvapení zde, jen potvrzení, že můj stavební kód výše generoval očekávanou velikost komentářů v každém postupu:
Postup | Velikost (bajty) |
---|---|
Small_Separate / Small_Embedded | 378 |
Středně_oddělené / Střední_vložené | 4 340 |
Large_Separate / Large_Separate | 40 338 |
ExtraLarge_Separate / ExtraLarge_Separate | 400 348 |
Další, jak velké byly plány v mezipaměti?
Postup | Velikost (bajty) |
---|---|
Small_Separate / Small_Embedded | 40 360 |
Středně_oddělené / Střední_vložené | 40 360 |
Large_Separate / Large_Separate | 40 360 |
ExtraLarge_Separate / ExtraLarge_Separate | 40 360 |
Jaké bylo nakonec vystoupení? Bez OPTION (RECOMPILE)
, zde je průměrná doba provádění v milisekundách – celkem konzistentní ve všech procedurách:
Průměrná doba trvání (milisekundy) – bez OPTION (RECOMPILE)
S OPTION (RECOMPILE)
na úrovni příkazu , můžeme zaznamenat asi 50% zásah v průměrné době trvání ve srovnání s žádnou rekompilací, ale stále docela vyrovnaný:
Průměrná doba trvání (milisekundy) – s OPTION (REKOMPILOVAT)
V obou případech, zatímco OPTION (RECOMPILE)
verze obecně běžela pomaleji, byla prakticky NULA rozdíl v době běhu, bez ohledu na velikost komentáře v těle procedury.
A co vyšší náklady na kompilaci?
Dále jsem chtěl zjistit, zda by tyto velké komentáře měly obrovský dopad na náklady na kompilaci, například kdyby byly procedury vytvořeny WITH RECOMPILE
. Výše uvedený stavební předpis bylo snadné změnit, aby to zohlednil. Ale v tomto případě jsem se nemohl spolehnout na sys.dm_exec_procedure_stats
, protože to nefunguje pro procedury WITH RECOMPILE
. Můj generační kód pro test byl tedy trochu jiný, protože průměrnou dobu trvání bych musel sledovat ručně:
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;
V tomto případě jsem nemohl zkontrolovat velikost plánů v mezipaměti, ale byl jsem schopen určit průměrnou dobu běhu procedur a byl zde rozdíl na základě velikosti komentáře (nebo možná jen velikosti těla procedury):
Průměrná doba trvání (milisekundy) – S PŘEKOMPILOVÁNÍM na úrovni procedur
Pokud je všechny dáme dohromady do grafu, je jasné, o kolik dražší je WITH RECOMPILE
použití může být:
Průměrná doba trvání (milisekundy) – porovnání všech tří metod
Pravděpodobně se na to blíže podívám později, abych přesně viděl, kde tato hokejka vstupuje do hry – představuji si testování v krocích po 10 000 znacích. Zatím jsem ale docela spokojený, že jsem na otázku odpověděl.
Shrnutí
Zdá se, že komentáře zcela nesouvisí se skutečným, pozorovatelným výkonem uložené procedury, s výjimkou případu, kdy je procedura definována WITH RECOMPILE
. Osobně už to ve volné přírodě nevidím, ale YMMV. Pro jemné rozdíly mezi touto volbou a na úrovni příkazů OPTION (RECOMPILE)
, viz článek Paula Whitea, "Parameter Sniffing, Embedding, and the RECOMPILE Options."
Osobně si myslím, že komentáře mohou být nesmírně cenné pro každého, kdo musí kontrolovat, udržovat nebo odstraňovat problémy s vaším kódem. To zahrnuje i vás budoucí. Vřele nedoporučuji se obávat o dopad přiměřeného množství komentářů na výkon a místo toho se zaměřit na upřednostnění užitečnosti kontextu, který komentáře poskytují. Jak někdo na Twitteru řekl, existuje limit. Pokud se vaše komentáře týkají zkrácené verze War and Peace, můžete zvážit – s rizikem oddělení kódu od jeho dokumentace – umístění této dokumentace jinam a odkazovat na odkaz v komentářích těla procedury.
Chcete-li minimalizovat riziko oddělení nebo jinak synchronizace dokumentace a kódu v průběhu času, můžete vytvořit druhou proceduru s příponou _documentation
nebo _comments
a vložte tam komentáře (nebo komentovanou verzi kódu). Možná to dát do jiného schématu, aby to nebylo z hlavních třídicích seznamů. Přinejmenším dokumentace zůstává s databází, ať jde kamkoli, i když nezaručuje, že bude udržována. Je nešťastné, že normální postup nelze vytvořit WITH SCHEMABINDING
, v takovém případě byste mohli připomínkové řízení výslovně spojit se zdrojem.