將(jiang)不(bu)確(que)定變為確(que)定~老(lao)趙寫的CodeTimer是代碼性能(neng)測試的利器
首先,非(fei)常(chang)感謝(xie)趙(zhao)老大的CodeTimer,它讓(rang)我們更(geng)好的了解到代(dai)碼執行的性能(neng)(neng),從(cong)而可以讓(rang)我們從(cong)性能(neng)(neng)的角度來考(kao)慮問(wen)題,有些(xie)東(dong)西可能(neng)(neng)我們認為(wei)是這(zhe)樣的,但經(jing)理測試并非(fei)如何(he),這(zhe)正應了我之前(qian)的那名話(hua):“機器(qi)最能(neng)(neng)證明一切(qie)”!
費(fei)話就不說了,看代碼吧:
1 /// <summary> 2 /// 執行(xing)代碼規范 3 /// </summary> 4 public interface IAction 5 { 6 void Action(); 7 } 8 9 /// <summary> 10 /// 老趙的性能測(ce)試(shi)工具(ju) 11 /// </summary> 12 public static class CodeTimer 13 { 14 [DllImport("kernel32.dll", SetLastError = true)] 15 static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime); 16 17 [DllImport("kernel32.dll")] 18 static extern IntPtr GetCurrentThread(); 19 public delegate void ActionDelegate(); 20 private static long GetCurrentThreadTimes() 21 { 22 long l; 23 long kernelTime, userTimer; 24 GetThreadTimes(GetCurrentThread(), out l, out l, out kernelTime, out userTimer); 25 return kernelTime + userTimer; 26 } 27 static CodeTimer() 28 { 29 Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; 30 Thread.CurrentThread.Priority = ThreadPriority.Highest; 31 } 32 public static void Time(string name, int iteration, ActionDelegate action) 33 { 34 if (String.IsNullOrEmpty(name)) 35 { 36 return; 37 } 38 if (action == null) 39 { 40 return; 41 } 42 43 //1. Print name 44 ConsoleColor currentForeColor = Console.ForegroundColor; 45 Console.ForegroundColor = ConsoleColor.Yellow; 46 Console.WriteLine(name); 47 48 // 2. Record the latest GC counts 49 //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); 50 GC.Collect(GC.MaxGeneration); 51 int[] gcCounts = new int[GC.MaxGeneration + 1]; 52 for (int i = 0; i <= GC.MaxGeneration; i++) 53 { 54 gcCounts[i] = GC.CollectionCount(i); 55 } 56 57 // 3. Run action 58 Stopwatch watch = new Stopwatch(); 59 watch.Start(); 60 long ticksFst = GetCurrentThreadTimes(); //100 nanosecond one tick 61 for (int i = 0; i < iteration; i++) action(); 62 long ticks = GetCurrentThreadTimes() - ticksFst; 63 watch.Stop(); 64 65 // 4. Print CPU 66 Console.ForegroundColor = currentForeColor; 67 Console.WriteLine("\tTime Elapsed:\t\t" + 68 watch.ElapsedMilliseconds.ToString("N0") + "ms"); 69 Console.WriteLine("\tTime Elapsed (one time):" + 70 (watch.ElapsedMilliseconds / iteration).ToString("N0") + "ms"); 71 Console.WriteLine("\tCPU time:\t\t" + (ticks * 100).ToString("N0") 72 + "ns"); 73 Console.WriteLine("\tCPU time (one time):\t" + (ticks * 100 / 74 iteration).ToString("N0") + "ns"); 75 76 // 5. Print GC 77 for (int i = 0; i <= GC.MaxGeneration; i++) 78 { 79 int count = GC.CollectionCount(i) - gcCounts[i]; 80 Console.WriteLine("\tGen " + i + ": \t\t\t" + count); 81 } 82 Console.WriteLine(); 83 } 84 85 86 87 public static void Time(string name, int iteration, IAction action) 88 { 89 if (String.IsNullOrEmpty(name)) 90 { 91 return; 92 } 93 94 if (action == null) 95 { 96 return; 97 } 98 99 //1. Print name 100 ConsoleColor currentForeColor = Console.ForegroundColor; 101 Console.ForegroundColor = ConsoleColor.Yellow; 102 Console.WriteLine(name); 103 104 // 2. Record the latest GC counts 105 //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); 106 GC.Collect(GC.MaxGeneration); 107 int[] gcCounts = new int[GC.MaxGeneration + 1]; 108 for (int i = 0; i <= GC.MaxGeneration; i++) 109 { 110 gcCounts[i] = GC.CollectionCount(i); 111 } 112 113 // 3. Run action 114 Stopwatch watch = new Stopwatch(); 115 watch.Start(); 116 long ticksFst = GetCurrentThreadTimes(); //100 nanosecond one tick 117 for (int i = 0; i < iteration; i++) action.Action(); 118 long ticks = GetCurrentThreadTimes() - ticksFst; 119 watch.Stop(); 120 121 // 4. Print CPU 122 Console.ForegroundColor = currentForeColor; 123 Console.WriteLine("\tTime Elapsed:\t\t" + 124 watch.ElapsedMilliseconds.ToString("N0") + "ms"); 125 Console.WriteLine("\tTime Elapsed (one time):" + 126 (watch.ElapsedMilliseconds / iteration).ToString("N0") + "ms"); 127 Console.WriteLine("\tCPU time:\t\t" + (ticks * 100).ToString("N0") 128 + "ns"); 129 Console.WriteLine("\tCPU time (one time):\t" + (ticks * 100 / 130 iteration).ToString("N0") + "ns"); 131 132 // 5. Print GC 133 for (int i = 0; i <= GC.MaxGeneration; i++) 134 { 135 int count = GC.CollectionCount(i) - gcCounts[i]; 136 Console.WriteLine("\tGen " + i + ": \t\t\t" + count); 137 } 138 Console.WriteLine(); 139 } 140 }
有(you)了上面的codeTimer我們(men)就(jiu)來測試一個吧(ba),如(ru)字條串和并的問題,用+=還(huan)是(shi)用StringBuilder呢,有(you)點經驗的程序員肯定(ding)說是(shi)StringBuilder,是(shi)的,確實是(shi)后者,那我們(men)就(jiu)來看看這
兩種方法測(ce)試的結(jie)果(guo)吧
1 CodeTimer.Time("String Concat", 100000, 2 () => 3 { 4 var s = "1"; 5 for (int i = 1; i < 10; i++) 6 s = s + "1"; 7 }); 8 9 CodeTimer.Time("StringBuilder Concat", 100000, 10 () => 11 { 12 var s = new StringBuilder(); 13 for (int i = 1; i < 10; i++) 14 s.Append("1"); 15 });
測試的結果如下:
從(cong)圖中我們(men)可以看到StringBuilder快的很明顯,無論是(shi)執行時(shi)間,還是(shi)對CPU的消耗及GC回收都(dou)遠(yuan)低于String的拼結,所以,才有(you)以下結論: