我们在开发的过程中,很多时候一个功能可能有多个实现方法,为了追求代码的性能,我们往往需要比较各实现方法的运行时间,从而选择性能最好的实现方法。那么怎样计算一段代码(或者说是函数)的运行时间呢,这个就是这篇文章我们要总结的内容。我们主要分以下几点来总结。
- 在C#代码中计算代码执行时间
- 在Sql Server中计算代码执行时间
在C#代码中计算代码执行时间
在C#程序中要计算代码段(或方法)的执行时间,我们一般都使用Stopwatch类,我比较了使用+=和使用StringBuilder分别拼接字符串的性能差异,示例代码如下。
1 namespace ConsoleApplication5 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 // 初始化性能计数器 8 CodeTimer.Initialize(); 9 10 // 定义执行次数 11 int iteration = 100 * 1000; //10万次 12 13 string s = string.Empty; 14 CodeTimer.Time("String Concat", iteration, () => 15 { 16 s += "a"; 17 }); 18 19 StringBuilder sb = new StringBuilder(); 20 CodeTimer.Time("StringBuilder", iteration, () => 21 { 22 sb.Append("a"); 23 }); 24 25 Console.ReadKey(); 26 } 27 } 28 }
运行结果如下图。
我这里使用了封装的一个性能计时器,文章后面会附上源代码。
在Sql Server中计算代码执行时间
sql server中一般使用GetDate和DateDiff函数来计算sql语句运行的时间,示例代码如下。
1 USE PackageFHDB; 2 GO 3 4 -- 开始时间 5 DECLARE @t1 AS DATETIME; 6 SELECT @t1= GETDATE(); 7 8 -- 运行的sql语句 9 SELECT TOP 1000 * FROM dbo.Pkg_PkgOrderMaster; 10 11 -- 打印结果 12 PRINT DATEDIFF(millisecond,@t1,GETDATE());
执行结果为293毫秒,如下图。
附:性能计时器的源代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 using LNFramework.Common.Extends; 8 using System.Runtime.InteropServices; 9 10 namespace LNFramework.Common.Tools 11 { 12 /// <summary> 13 /// 性能计时器 14 /// </summary> 15 public static class CodeTimer 16 { 17 /// <summary> 18 /// 初始化 19 /// </summary> 20 public static void Initialize() 21 { 22 // 将当前进程及线程的优先级设为最高,减少操作系统在调度上造成的干扰 23 // 然后调用一次Time方法进行预热,以便让Time方法尽快进入状态 24 Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; 25 Thread.CurrentThread.Priority = ThreadPriority.Highest; 26 Time("", 1, () => { }); 27 } 28 29 /// <summary> 30 /// 计时 31 /// </summary> 32 /// <param name="name">名称</param> 33 /// <param name="iteration">循环次数</param> 34 /// <param name="action">方法体</param> 35 public static void Time(string name, int iteration, Action action) 36 { 37 if (name.IsNullOrEmpty()) return; 38 39 // 1.保留当前控制台前景色,并使用黄色输出名称参数 40 ConsoleColor currentForeColor = Console.ForegroundColor; 41 Console.ForegroundColor = ConsoleColor.Yellow; 42 Console.WriteLine(name); 43 44 // 2.强制GC进行收集,并记录目前各代已经收集的次数 45 GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); 46 int[] gcCounts = new int[GC.MaxGeneration + 1]; 47 for (int i = 0; i <= GC.MaxGeneration; i++) 48 { 49 gcCounts[i] = GC.CollectionCount(i); 50 } 51 52 // 3.执行代码,记录下消耗的时间及CPU时钟周期 53 Stopwatch watch = new Stopwatch(); 54 watch.Start(); 55 ulong cycleCount = GetCycleCount(); 56 for (int i = 0; i < iteration; i++) 57 { 58 action(); 59 } 60 ulong cpuCycles = GetCycleCount() - cycleCount; 61 watch.Stop(); 62 63 // 4.恢复控制台默认前景色,并打印出消耗时间及CPU时钟周期 64 Console.ForegroundColor = currentForeColor; 65 Console.WriteLine(" Time Elapsed: " + watch.ElapsedMilliseconds.ToString("N0") + "ms"); 66 Console.WriteLine(" CPU Cycles: " + cpuCycles.ToString("N0")); 67 68 // 5.打印执行过程中各代垃圾收集回收次数 69 for (int i = 0; i <= GC.MaxGeneration; i++) 70 { 71 int count = GC.CollectionCount(i) - gcCounts[i]; 72 Console.WriteLine(" Gen " + i + ": " + count); 73 } 74 75 Console.WriteLine(); 76 } 77 78 private static ulong GetCycleCount() 79 { 80 ulong cycleCount = 0; 81 QueryThreadCycleTime(GetCurrentThread(), ref cycleCount); 82 return cycleCount; 83 } 84 85 [DllImport("kernel32.dll")] 86 [return: MarshalAs(UnmanagedType.Bool)] 87 static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime); 88 89 [DllImport("kernel32.dll")] 90 static extern IntPtr GetCurrentThread(); 91 } 92 }
参考文章: