With data and truth, we believe that we need to compare the performance gaps between several different methods or implementations during our daily work or study. In these cases, we often need to constantly create stopwatch, open, close, and print the time. This type of repetition over and over again will be intolerable one day. Therefore, if there is a "standard" performance counter, it should be much easier. This performance counter does not need to be complex. It is enough. You do not need to consider scalability. You need to modify it directly when you want to scale it.CodeThat's enough; similarly, you don't need to consider the output format, just print it on the console.
In the last. at the net technology conference, Uncle Jeffrey Richter gave a keynote speech entitled "The performance of everyday things" in the keynote session, demonstrating the performance comparison between various common programming elements. In the demonstration, he used a simple counter named codetimer to measure the performance of each practice. Unfortunately, I did not find out where Uncle JR made public the implementation of this counter. Forget it, let's write it out with an impression. It's not complicated.
In general, codetimer has two public methods: Initialize and time:
Public static class codetimer {public static void initialize () {process. getcurrentprocess (). priorityclass = processpriorityclass. high; thread. currentthread. priority = threadpriority. highest; time ("", 1, () =>{});} public static void time (string name, int iteration, action Action ){...}}
the codetimer. initialize method should be called before the test starts. First, it sets the priority of the current process and the current thread to the highest, which can relatively reduce the interference caused by the operating system scheduling. Then, call the time method to perform "push", let JIT compile the Il code at the cost, and let the time method "Enter the state" as soon as possible ". The time method is actually used for performance counting. The implementation is as follows:
Public static void time (string name, int iteration, action Action) {If (string. isnullorempty (name) return; // 1. lelecolor currentforecolor = console. foregroundcolor; console. foregroundcolor = consolecolor. yellow; console. writeline (name); // 2. GC. collect (GC. maxgeneration, gccollectionmode. forced); int [] gccounts = new int [GC. maxgeneration + 1]; for (INT I = 0; I <= GC. maxgeneration; I ++) {gccounts [I] = GC. collectioncount (I);} // 3. stopwatch watch = new stopwatch (); watch. start (); ulong cyclecount = getcyclecount (); For (INT I = 0; I <iteration; I ++) Action (); ulong cpucycles = getcyclecount ()-cyclecount; watch. stop (); // 4. console. foregroundcolor = currentforecolor; console. writeline ("\ tTime elapsed: \ t" + watch. elapsedmilliseconds. tostring ("N0") + "Ms"); console. writeline ("\ tcpu cycles: \ t" + cpucycles. tostring ("N0"); // 5. for (INT I = 0; I <= GC. maxgeneration; I ++) {int COUNT = GC. collectioncount (I)-gccounts [I]; console. writeline ("\ tgen" + I + ": \ t" + count);} console. writeline ();} Private Static ulong getcyclecount () {ulong cyclecount = 0; querythreadcycletime (getcurrentthread (), ref cyclecount); Return cyclecount;} [dllimport ("kernel32.dll")] [Return: financialas (unmanagedtype. bool)] Static extern bool querythreadcycletime (intptr threadhandle, ref ulong cycletime); [dllimport ("kernel32.dll")] Static extern intptr getcurrentthread ();
The time method accepts three parameters: Name, number of cycles, and method body to be executed. Print the time spent, the CPU clock cycle consumed, and the number of garbage collection times. The specific implementation steps are as follows:
- Retain the foreground color of the current console and use the yellow output name parameter.
- Force GC to collect and record the number of times collected by generation.
- Run the code to record the consumed time and CPU clock cycle 1.
- Restore the default foreground color of the console and print the consumed time and CPU clock cycle.
- Print the number of garbage collection times during execution.
Compared with the traditional counting method, this code also outputs more information: the CPU clock cycle and the number of garbage collection times of each generation. The CPU clock cycle is an auxiliary reference in the performance count. It shows the number of time slice allocated by the CPU to this method. It is not necessarily related to the consumed time. For example, the thread. Sleep method will temporarily stop the CPU from "Supplying" to the current thread, which consumes time but saves the CPU clock cycle:
Codetimer. time ("thread sleep", 1, () => {thread. sleep (3000) ;}); codetimer. time ("Empty method", 10000000, () => {});
The result is as follows:
The statistics on the number of garbage collection times intuitively reflect the scale of method resource allocation (consumption:
Int iteration = 100*1000; string S = ""; codetimer. time ("string Concat", iteration, () => {S + = "A" ;}); stringbuilder sb = new stringbuilder (); codetimer. time ("stringbuilder", iteration, () => {sb. append ("");});
The result is as follows:
Lao Zhao has recently studied the performance advantages and disadvantages of several different practices on a problem. Among them, codetimer plays a very important role-here we also sell a pair of secrets, and then Lao Zhao will write several articles.ArticleTo explain this problem.
Note 1: When calculating the CPU clock cycle, use P/invoke to access the querythreadcycletime function, which is a new function in Vista and Server 2008. Thanks for the help of Assembly head.
NOTE 2: For. NET 2.0 and earlier Vista operating systems, see modifying simple performance counters written by Lao Zhao