有数据,有真相,相信大家在平时的工作或学习过程中,都需要比较几种不同方法或实现之间的性能差距。在这些时候,往往就需要我们不断地创建Stopwatch,打开,关闭,然后打印时间。这种一遍又一遍的重复终有一天会让人忍无可忍,因此如果能有一个“标准”的性能计数器,那应该可以让生活轻松许多。这个性能计数器不用复杂,够用就好;也不需要考虑扩展性,要扩展时直接修改代码就够了;同样不需要考虑输出格式,直接打印在Console就行。
在上次的.NET技术大会中,Jeffrey Richter大叔在Keynote Session中进行了一个名为“The Performance of Everyday Things”的主题演讲,展示了各种常用编程元素之间的性能对比。在演示中他使用了一个名为CodeTimer的简单计数器,用于统计每种做法的性能。可惜翻遍了每个地方都没发现JR大叔在哪里公开了这个计数器的实现。算了,那么就凭着印象写一个出来吧,反正也不复杂。
总的来说,CodeTimer有两个公开方法,一个是Initialize,一个是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)
{
...
}
}
CodeTimer.Initialize方法应该在测试开始前调用。首先它会把当前进程及当前线程的优先级设为最高,这样便可以相对减少操作系统在调度上造成的干扰。然后调用一次Time方法进行“预热”,让JIT将IL编译成本地代码,让Time方法尽快“进入状态”。Time方法则是真正用于性能计数的方法,实现如下:
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.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\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();
Time方法接受三个参数,名称,循环次数以及需要执行的方法体。打印出花费时间,消耗的CPU时钟周期,以及各代垃圾收集的回收次数。具体实现分几个步骤,如下:
- 保留当前控制台前景色,并使用黄色输出名称参数。
- 强制GC进行收集,并记录目前各代已经收集的次数。
- 执行代码,记录下消耗的时间及CPU时钟周期1。
- 恢复控制台默认前景色,并打印出消耗时间及CPU时钟周期。
- 打印执行过程中各代垃圾收集回收次数。
与传统计数方法相比,这段代码还输出了更多信息:CPU时钟周期及各代垃圾收集回收次数。CPU时钟周期是性能计数中的辅助参考,说明CPU分配了多少时间片给这段方法来执行,它和消耗时间并没有必然联系。例如Thread.Sleep方法会让CPU暂时停止对当前线程的“供给”,这样虽然消耗了时间,但是节省了CPU时钟周期:
CodeTimer.Time("Thread Sleep", 1, () => { Thread.Sleep(3000); });
CodeTimer.Time("Empty Method", 10000000, () => { });
结果如下:
而垃圾收集次数的统计,即直观地反应了方法资源分配(消耗)的规模:
int iteration = 100 * 1000;
string s = "";
CodeTimer.Time("String Concat", iteration, () => { s += "a"; });
StringBuilder sb = new StringBuilder();
CodeTimer.Time("StringBuilder", iteration, () => { sb.Append("a"); });
结果如下:
老赵最近在研究一个问题的几种不同做法在性能上的优劣,其中CodeTimer起到了很重要的作用——这边也先卖个关子,接下来老赵也将会写几篇文章来讲解这个问题。
注1:统计CPU时钟周期时使用P/Invoke访问QueryThreadCycleTime函数,这是Vista和Server 2008中新的函数。感谢装配脑袋在这里提供的帮助。
注2:对于.NET 2.0及Vista以下操作系统,请参考《对老赵写的简单性能计数器的修改》
分享到:
相关推荐
性能监视器计数器是 Windows 操作系统中的一种性能监视工具,用于监视系统的性能和资源使用情况。它可以提供详细的性能数据,帮助管理员更好地监控和优化系统性能。在安装 SQL Server 2005 时,性能监视器计数器的...
在Windows操作系统中,性能计数器是一个强大的工具,可以用于实时监控系统各项性能指标,包括网络流量。"Windows性能计数器统计网络流量.rar"这个压缩包文件显然提供了关于如何利用这一功能来追踪和分析网络活动的...
性能计数器是操作系统提供的一个接口,允许应用程序发布关于其性能的数据。这些数据可以是瞬时值,也可以是随时间变化的趋势。通过Windows的任务管理器或者第三方监控工具,我们可以查看和分析这些计数器,以便优化...
文件“CodeTimer”很可能包含了一个实现这些功能的简单类或模块。可能的实现方式包括使用编程语言提供的内置性能测量API(如Python的`time`模块或Java的`System.nanoTime()`),或者自定义计时逻辑来跟踪上述指标。...
下面是一个简单的示例代码片段,展示如何在ASP中创建并读取一个性能计数器: ```csharp using System; using System.Diagnostics; // 创建性能计数器 PerformanceCounter counter = new PerformanceCounter("ASP...
另一方面,应用程序性能计数器在ASP.NET Applications性能对象中公开,它们关注的是特定应用实例的性能。`Requests Queued`计数器显示等待服务的请求数量,当这个值持续增长,表明服务器可能达到了并发请求处理能力...
在Windows 7操作系统中,性能计数器是一个强大的工具,用于监控系统性能并诊断各种问题,如句柄泄漏和内存增长。性能计数器提供实时数据,这些数据可以通过图形化展示,帮助用户直观地理解系统的运行状态。下面将...
SQL Server 2005 安装问题中,性能监视器计数器要求是一个常见的错误。解决此问题需要了解注册表编辑器和Performance Monitor计数器的概念。 注册表编辑器 注册表编辑器是一个强大的工具,用于编辑和修改系统...
每个性能计数器都有一个唯一的类别,比如"Processor"(处理器)、"Memory"(内存)等,并且每个类别下有许多实例,比如对于"Processor",每个CPU核心就是一个实例。 在C#中,我们可以使用`System.Diagnostics....
Visual C++源代码 74 如何启动操作系统性能计数器Visual C++源代码 74 如何启动操作系统性能计数器Visual C++源代码 74 如何启动操作系统性能计数器Visual C++源代码 74 如何启动操作系统性能计数器Visual C++源代码...
性能监视器计数器要求错误是一个常见的安装错误,特别是在安装SQL Server 2005时。这种错误的出现可能是由于之前安装的SQL Server 2005 Express版本的残留文件或注册表项引起的。今天,我们将讨论这个错误的解决方案...
"解决 SQL Server 2008 安装时“性能计数器注册表配置单元一致性”错误" 在安装 SQL Server 2008 时,可能会遇到“性能计数器注册表配置单元一致性”错误,这是由于注册表中的配置问题导致的。解决这个问题需要了解...
- **"本文档为IIS性能计数器错误时的操作步骤,内容简洁明了"**:这表明文档的主要内容是提供一系列简单易懂的步骤来帮助用户解决IIS性能计数器出现错误的问题。 #### 标签解析 - **"计数器"**:进一步强调文档的...
本资源聚焦于一个特定的VHDL特性——`generate`语句,它在设计计数器时发挥着重要作用。计数器是数字系统中的基本组件,常见于各种应用场景,如定时器、分频器和地址发生器等。 `generate`语句是VHDL中的并行构造...
每个性能计数器都有一个唯一的名称,比如"\Processor(_Total)\% Processor Time",表示所有处理器的平均CPU使用率。计数器类型有多种,如计数器(Counter)、计数器实例(Instance)和性能对象(Performance Object...
Windows性能计数器是Windows操作系统中的一个重要工具,它允许用户实时监控系统性能,包括CPU、内存、硬盘I/O、网络等各个方面的数据。这个工具对于系统管理员和开发者来说非常实用,因为它可以提供详尽的系统资源...
本资源包含三个YOLOv8制作的计数器: 1.客流检测器:使用OpenCV检测顾客,设定客人进出区域,实时计算进出顾客的数量。 2.鸡蛋计数器:使用OpenCV和YOLOv8检测鸡蛋个数,高效准确完成鸡蛋个数的统计工作。 3.停车位...