一个简单的性能计数器

前言

     最近在网上看到了老赵的这篇文章。自己也经常要用到性能测试,所以就在此记录下,方便以后查阅。

     有数据,有真相,相信大家在平时的工作或学习过程中,都需要比较几种不同方法或实现之间的性能差距。在这些时候,往往就需要我们不断地创建Stopwatch,打开,关闭,然后打印时间。这种一遍又一遍的重复终有一天会让人忍无可忍,因此如果能有一个“标准”的性能计数器,那应该可以让生活轻松许多。这个性能计数器不用复杂,够用就好;也不需要考虑扩展性,要扩展时直接修改代码就够了;同样不需要考虑输出格式,直接打印在Console就行。

实现代码

      总的来说,CodeTimer有两个公开方法,一个是Initialize,一个是Time:

<span style="font-family:Microsoft YaHei;">  public static void Initialize()
        {
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
            Thread.CurrentThread.Priority = ThreadPriority.Highest;
            Time("", 1, () => { });
        }</span>

<span style="font-family:Microsoft YaHei;">  /// <summary>
        /// 用于性能计数的方法
        /// </summary>
        /// <param name="name">名称</param>
        /// <param name="iteration">循环次数</param>
        /// <param name="action">需要执行的方法</param>
        public static void Time(string name, int iteration, Action action)
        {
            if (String.IsNullOrEmpty(name)) return;

            // 1.保留当前控制台前景色,并使用黄色输出名称参数
            ConsoleColor currentForeColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(name);

            // 2.强制GC进行收集,并记录目前各代已经收集的次数
            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.执行代码,记录下消耗的时间及CPU时钟周期
            Stopwatch watch = new Stopwatch();
            watch.Start();
            ulong cycleCount = GetCycleCount();
            for (int i = 0; i < iteration; i++) action();
            ulong cpuCycles = GetCycleCount() - cycleCount;
            watch.Stop();

            // 4.恢复控制台默认前景色,并打印出消耗时间及CPU时钟周期
            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\t" + count);
            }

            Console.WriteLine();
        }
        private static ulong GetCycleCount()
        {
            ulong cycleCount = 0;
            QueryThreadCycleTime(GetCurrentThread(), ref cycleCount);
            return cycleCount;
        }

        [DllImport("kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentThread();
    }</span>
CodeTimer.Initialize方法应该在测试开始前调用。首先它会把当前进程及当前线程的优先级设为最高,这样便可以相对减少操作系统在调度上造成的干扰。然后调用一次Time方法进行“预热”,让JIT将IL编译成本地代码,让Time方法尽快“进入状态”。Time方法则是真正用于性能计数的方法。


Time方法接受三个参数,名称,循环次数以及需要执行的方法体。打印出花费时间,消耗的CPU时钟周期,以及各代垃圾收集的回收次数。具体实现分几个步骤,如下:
1、保留当前控制台前景色,并使用黄色输出名称参数。
2、强制GC进行收集,并记录目前各代已经收集的次数。
3、执行代码,记录下消耗的时间及CPU时钟周期1。
4、恢复控制台默认前景色,并打印出消耗时间及CPU时钟周期。
5、打印执行过程中各代垃圾收集回收次数。
与传统计数方法相比,这段代码还输出了更多信息:CPU时钟周期及各代垃圾收集回收次数。CPU时钟周期是性能计数中的辅助参考,说明CPU分配了多少时间片给这段方法来执行,它和消耗时间并没有必然联系。例如Thread.Sleep方法会让CPU暂时停止对当前线程的“供给”,这样虽然消耗了时间,但是节省了CPU时钟周期:

<span style="font-family:Microsoft YaHei;">CodeTimer.Time("Thread Sleep", 1, () => { Thread.Sleep(3000); });
CodeTimer.Time("Empty Method", 10000000, () => { });</span>

结果如下:


而垃圾收集次数的统计,即直观地反应了方法资源分配(消耗)的规模:

<span style="font-family:Microsoft YaHei;">int iteration = 100 * 1000;

string s = "";
CodeTimer.Time("String Concat", iteration, () => { s += "a"; });

StringBuilder sb = new StringBuilder();
CodeTimer.Time("StringBuilder", iteration, () => { sb.Append("a"); });</span>

结果如下:


其他

原文链接:点击打开链接

源码下载:点击打开链接


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
解决办法就是重新生成性能计数器库值,具体如下: 1)从Window XP 或 Windows 2003 光盘中提出"PERFC009.DA_文件"PERFH009.DA_"文件,通常它们位于光盘的 \i386 目录中,然后进入cdm,键入下面的命令分别展开它们: expand perfc009.da_ expand perfh009.da_ 然后把展开出来的 perfc009.dat 和 perfh009.dat 替换 windows\system32\ 下的同名文件。    展开方式如下:    2)启动注册表编辑器,然后在注册表中查找以下项: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Perflib 将"LastCounter"值更改为 1846(十进制),并将"LastHelp"值更改为 1847(十进制)。    3)继续找到以下注册表项 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services 如果存在 Performance 子项,则删除以下值: ? FirstCounter ? FirstHelp ? LastCounter ? LastHelp    4)打开cmd, 键入 cd %Systemroot%\System32 findstr drivername *.ini 然后按 Enter。 注意列表中每个驱动程序名称所对应的 .ini 文件名。 在命令提示符处,键入下面一行,然后按 Enter: lodctr inifile 其中,inifile 是对应您要重新加载的驱动程序的 .ini 文件名。 例如,如果打算重新加载 ASP 驱动程序,则第 4 步中出现的列表将显示 Axperf.ini 是用于 ASP 驱动程序的 .ini 文件 (axperf.ini:drivername=ASP)。因此,要重新加载 ASP 驱动程序,请在命令提示符处键入 lodctr axperf.ini,然后按 Enter。 下面我以 Windows XP 为例,把执行的步骤写上来,建一个bat文件,复制进去执行就可以了。 lodctr esentprf.ini lodctr mqperf.ini lodctr msdtcprf.ini lodctr perfci.ini lodctr perffilt.ini lodctr perfwci.ini lodctr pschdprf.ini lodctr rasctrs.ini lodctr rsvp.ini lodctr tslabels.ini pause    5)重新启动计算机。 最后还是cmd: cd \windows\system32 lodctr /R 注意:/R 是大写。 这时,已经可以看到性能计数器库值已完成了重新生成。   再来看看sql server 2008的安装,一切正常 细节和需要注意的地方,请参考微软支持(http://support.microsoft.com/kb/300956/zh-cn)   如果出现下面这种情况   退后两步,设置成 就行了   再回去看看 一切正常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值