Sesekali, percakapan muncul di mana orang-orang yakin bahwa komentar berpengaruh atau tidak berdampak pada kinerja.
Secara umum, saya akan mengatakan bahwa, tidak, komentar tidak memengaruhi kinerja , tetapi selalu ada ruang untuk penafian "itu tergantung". Mari kita buat database sampel dan tabel yang penuh dengan sampah:
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
Sekarang, saya ingin membuat empat prosedur tersimpan – satu dengan 20 karakter komentar, satu dengan 2000, satu dengan 20.000, dan satu dengan 200.000. Dan saya ingin melakukannya lagi di mana komentar disematkan * di dalam * pernyataan kueri dalam prosedur, sebagai lawan dari independen (yang akan berpengaruh pada paket XML). Akhirnya, saya mengulangi proses menambahkan OPTION (RECOMPILE)
ke kueri.
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;
Sekarang, saya perlu membuat kode untuk menjalankan setiap prosedur 100.000 kali, mengukur durasi dari sys.dm_exec_procedure_stats
, dan juga periksa ukuran paket di 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;
Pertama, mari kita lihat ukuran badan prosedur. Tidak ada kejutan di sini, hanya mengonfirmasi bahwa kode konstruksi saya di atas menghasilkan ukuran komentar yang diharapkan di setiap prosedur:
Selanjutnya, seberapa besar paket yang ada di cache?
Akhirnya, seperti apa penampilannya? Tanpa OPTION (RECOMPILE)
, berikut adalah waktu eksekusi rata-rata, dalam milidetik – cukup konsisten di semua prosedur:
Durasi rata-rata (milidetik) – tanpa OPSI (REKOMPILASI)
Dengan OPTION (RECOMPILE)
tingkat pernyataan , kita dapat melihat sekitar 50% hit dalam durasi rata-rata di seluruh papan dibandingkan tanpa kompilasi ulang, tetapi masih cukup merata:
Durasi rata-rata (milidetik) – dengan OPTION (RECOMPILE)
Dalam kedua kasus, sementara OPTION (RECOMPILE)
versi umumnya berjalan lebih lambat, hampir ada ZERO perbedaan runtime, terlepas dari ukuran komentar di badan prosedur.
Bagaimana dengan biaya kompilasi yang lebih tinggi?
Selanjutnya, saya ingin melihat apakah komentar besar ini akan berdampak besar pada biaya kompilasi, misalnya jika prosedur dibuat WITH RECOMPILE
. Kode konstruksi di atas mudah diubah untuk memperhitungkan ini. Tapi dalam kasus ini, saya tidak bisa mengandalkan sys.dm_exec_procedure_stats
, karena ini tidak berfungsi untuk prosedur WITH RECOMPILE
. Jadi kode generasi saya untuk pengujian ini sedikit berbeda, karena saya harus melacak durasi rata-rata secara manual:
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;
Dalam hal ini, saya tidak dapat memeriksa ukuran paket dalam cache, tetapi saya dapat menentukan rata-rata runtime prosedur, dan ada perbedaan berdasarkan ukuran komentar (atau, mungkin, hanya ukuran tubuh prosedur):
Durasi rata-rata (milidetik) – DENGAN REKOMPILASI pada tingkat prosedur
Jika kita menggabungkan semuanya dalam sebuah grafik, jelas betapa lebih mahalnya WITH RECOMPILE
penggunaan dapat:
Durasi rata-rata (milidetik) – membandingkan ketiga metode
Saya mungkin akan melihat ini lebih dekat di lain waktu untuk melihat dengan tepat di mana tongkat hoki itu berperan – saya membayangkan pengujian dalam peningkatan 10.000 karakter. Namun, untuk saat ini, saya cukup puas telah menjawab pertanyaan tersebut.
Ringkasan
Komentar tampaknya sama sekali tidak terkait dengan kinerja prosedur tersimpan yang sebenarnya dan dapat diamati, kecuali dalam kasus di mana prosedur didefinisikan WITH RECOMPILE
. Secara pribadi, saya tidak melihat ini digunakan di alam liar lagi, tetapi YMMV. Untuk perbedaan halus antara opsi ini dan OPTION (RECOMPILE)
tingkat pernyataan , lihat artikel Paul White, "Parameter Sniffing, Embedding, and the RECOMPILE Options."
Secara pribadi, saya pikir komentar bisa sangat berharga bagi siapa saja yang harus meninjau, memelihara, atau memecahkan masalah kode Anda. Ini termasuk masa depan Anda. Saya sangat menyarankan untuk tidak mengkhawatirkan dampak kinerja dari jumlah komentar yang masuk akal, dan alih-alih fokus pada memprioritaskan kegunaan konteks yang diberikan komentar. Seperti yang dikatakan seseorang di Twitter, ada batasnya. Jika komentar Anda merupakan versi singkat dari War and Peace, Anda dapat mempertimbangkan – dengan risiko memisahkan kode dari dokumentasinya – meletakkan dokumentasi itu di tempat lain, dan merujuk tautan di komentar badan prosedur.
Untuk meminimalkan risiko decoupling, atau dokumentasi dan kode menjadi tidak sinkron dari waktu ke waktu, Anda dapat membuat prosedur kedua, dengan akhiran _documentation
atau _comments
, dan meletakkan komentar (atau versi kode yang dikomentari) di sana. Mungkin memasukkannya ke dalam skema yang berbeda untuk menjauhkannya dari daftar sortir utama. Setidaknya dokumentasi tetap dengan database ke mana pun ia pergi, meskipun tidak menjamin itu akan dipertahankan. Sangat disayangkan bahwa prosedur normal tidak dapat dibuat WITH SCHEMABINDING
, dalam hal ini Anda dapat secara eksplisit mengikat prosedur komentar ke sumbernya.