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)
{
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();
}

ime方法接受三个参数,名称,循环次数以及需要执行的方法体。打印出花费时间,消耗的CPU时钟周期,以及各代垃圾收集的回收次数。具体实现分几个步骤,如下:

  1. 保留当前控制台前景色,并使用黄色输出名称参数。
  2. 强制GC进行收集,并记录目前各代已经收集的次数。
  3. 执行代码,记录下消耗的时间及CPU时钟周期1
  4. 恢复控制台默认前景色,并打印出消耗时间及CPU时钟周期。
  5. 打印执行过程中各代垃圾收集回收次数。

与传统计数方法相比,这段代码还输出了更多信息:CPU时钟周期及各代垃圾收集回收次数。CPU时钟周期是性能计数中的辅助参考,说明CPU分配了多少时间片给这段方法来执行,它和消耗时间并没有必然联系。例如Thread.Sleep方法会让CPU暂时停止对当前线程的“供给”,这样虽然消耗了时间,但是节省了CPU时钟周期:

CodeTimer.Time("Thread Sleep", 1, () => { Thread.Sleep(3000); });
CodeTimer.Time("Empty Method", 10000000, () => { });

一个简单的性能计数器:CodeTimer

2009-03-10 09:03 by 老赵, 36062 visits

有数据,有真相,相信大家在平时的工作或学习过程中,都需要比较几种不同方法或实现之间的性能差距。在这些时候,往往就需要我们不断地创建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时钟周期,以及各代垃圾收集的回收次数。具体实现分几个步骤,如下:

  1. 保留当前控制台前景色,并使用黄色输出名称参数。
  2. 强制GC进行收集,并记录目前各代已经收集的次数。
  3. 执行代码,记录下消耗的时间及CPU时钟周期1
  4. 恢复控制台默认前景色,并打印出消耗时间及CPU时钟周期。
  5. 打印执行过程中各代垃圾收集回收次数。

与传统计数方法相比,这段代码还输出了更多信息: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起到了很重要的作用——这边也先卖个关子,接下来老赵也将会写几篇文章来讲解这个问题。

一个简单的性能计数器:CodeTimer的更多相关文章

  1. 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截

    程序猿修仙之路--数据结构之你是否真的懂数组?   数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构  .要想在之后的江湖历练中通关,数据结构必不可少. ...

  2. 哪种缓存效果高?开源一个简单的缓存组件j2cache

    背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...

  3. 在Openfire上弄一个简单的推送系统

    推送系统 说是推送系统有点大,其实就是一个消息广播功能吧.作用其实也就是由服务端接收到消息然后推送到订阅的客户端. 思路 对于推送最关键的是服务端向客户端发送数据,客户端向服务端订阅自己想要的消息.这 ...

  4. ASP.NET Aries 入门开发教程2:配置出一个简单的列表页面

    前言: 朋友们都期待我稳定地工作,但创业公司若要躺下,也非意念可控. 若人生注定了风雨飘摇,那就雨中前行了. 最机开始看聊新的工作机会,欢迎推荐,创业公司也可! 同时,趁着自由时间,抓紧把这系列教程给 ...

  5. 计算机程序的思维逻辑 (60) - 随机读写文件及其应用 - 实现一个简单的KV数据库

    57节介绍了字节流, 58节介绍了字符流,它们都是以流的方式读写文件,流的方式有几个限制: 要么读,要么写,不能同时读和写 不能随机读写,只能从头读到尾,且不能重复读,虽然通过缓冲可以实现部分重读,但 ...

  6. 如何开发一个简单的HTML5 Canvas 小游戏

    原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...

  7. CSharpGL(24)用ComputeShader实现一个简单的图像边缘检测功能

    CSharpGL(24)用ComputeShader实现一个简单的图像边缘检测功能 效果图 这是红宝书里的例子,在这个例子中,下述功能全部登场,因此这个例子可作为使用Compute Shader的典型 ...

  8. CSharpGL(23)用ComputeShader实现一个简单的ParticleSimulator

    CSharpGL(23)用ComputeShader实现一个简单的ParticleSimulator 我还没有用过Compute Shader,所以现在把红宝书里的例子拿来了,加入CSharpGL中. ...

  9. 应用OpenMP的一个简单的设计模式

    小喵的唠叨话:最近很久没写博客了,一是因为之前写的LSoftmax后馈一直没有成功,所以在等作者的源码.二是最近没什么想写的东西.前两天,在预处理图片的时候,发现处理200w张图片,跑了一晚上也才处理 ...

随机推荐

  1. 使用pybind11为Python编写C++扩展(一)配置篇:Build(编译和链接)

    目录 Setuptools CMake 最后决定选用pybind11,理由如下: 比python原生的C API看起来人性多了 我的C++代码不是现成的,需要一定的C++开发工作量,所以感觉cytho ...

  2. Spring专题2: DI,IOC 控制反转和依赖注入

    合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...

  3. Vue 之 浏览本地图片功能

      template <input type="file" ref="input_file" @change="fileChange" ...

  4. 从我做起[AutoMapper实现模块化注册自定义扩展MapTo<>()].Net Core 之二

    AutoMapper实现模块化注册自定义扩展MapTo<>() 我们都知道AutoMapper是使用的最多的实体模型映射,如果没有AutoMapper做对象映射那么我们需要想一下是怎么写的 ...

  5. Github上点赞90k的计算机基础、操作系统、网络笔记,赶紧收藏

    最近,有粉丝发消息给我,说想要好好学习计算机,但是找不到门路啊,所以小编打算给大家推荐一些很奈斯的计算机学习的资料,希望大家赶紧收藏起来好好学习. 资料分为四份,给大家展示了部分内容,头条受限,无法全 ...

  6. 【Android珍藏】推荐10个炫酷的开源库【转】

    感谢大佬:https://www.jianshu.com/p/d608f0228fd4 前言 技术群里面经常有人问到一些炫酷的UI效果实现方法,有时候我都是给一个相同或者相似效果的Github链接,有 ...

  7. JSP页面重定向与页面内容转发

    重定向:属于两次请求+响应,客户端浏览器地址栏会改变. 转发:属于一次请求+一次响应,客户端浏览器地址栏不会改变. 重定向: response.setHeader("action" ...

  8. DNS解析域名过程

    DNS解析域名过程 使用域名转换成IP地址,先读取本地HOST文件,本地文件没有从当前电信网管获取对应IP. 本地host文件 C:\Windows\System32\drivers\etc 画图演示 ...

  9. [转]API性能测试基本性能指标及要求

    原文链接http://blog.csdn.net/strawbingo/article/details/46458959 指标的基本概念 1.事务(Transaction) 在web性能测试中,一个事 ...

  10. 使用CAShapeLayer做出圆形的进度条 —— #DF!

    CircleView.h的内容如下: #import <UIKit/UIKit.h> @interface CircleView : UIView @property (nonatomic ...