我们有很多种方法评估一个方法的执行耗时,比如使用性能分析工具,使用基准性能测试。不过传统的在代码中编写计时的方式依然有效,因为它可以生产环境或用户端得到真实环境下的执行耗时。

如果你希望在 .NET/C# 代码中编写计时,那么阅读本文可以获得一些建议。阅读本文也可以了解到 QueryPerformanceCounterGet­System­Time­As­File­Time 等方法的差异。


基本的计时

计时一般采用下面这种方式,在方法执行之前获取一次时间,在方法结束之后再取得一次时间。

// 在方法开始之前。
Foo();
// 在方法执行之后。

这样,前后两次获取的时间差即为方法 Foo 的执行耗时。

这里我不会提到性能测试工具或者基准性能测试这些方法,因为这些测试代码不会运行于用户端。你可以阅读以下博客获得这两者的使用:

结论:使用什么方法计时

先说结论:System.Diagnostics 命名空间下有一个 Stopwatch 类。如果你要为你方法的执行时间进行统计,那么就使用这个类。

Stopwatch 类有一些静态属性、也有一些实例方法和实例属性。此类型的时间统计是按照高性能和高精度的要求来做的,于是你可以用它获得高精度的计时效果。不过,如果你对性能要求近乎苛刻,例如你的方法会被数百万次或更高频地执行,那么就需要开始斟酌如何调用里面的属性了。

简单的使用如下面这样:

var watch = Stopwatch.StartNew();
Foo();
watch.Stop();
var elapsed = watch.Elapsed;

当然,你也可以直接使用 Stopwatch 的构造函数,new 出来之后再 Start,不过 StartNew 静态方法可以将两句合并为一句。

各种计时 API 及其比较

计时还有很多的方法,你可以针对不同需求场景使用不同的方法。不过,如果你根本没有了解过其他方法的话,那么建议直接使用上面的 Stopwatch,不要想太多。

现在,我们看看 Windows 下的计时还有哪些 API:

  • 基于 QPC 的高精度 API

    • Query­Performance­Counter
    • Query­Performance­Frequency
  • 基于系统时间的非高精度 API
    • Get­Tick­Count, Get­Tick­Count64
    • Get­Message­Time
    • Get­System­Time, Get­Local­Time, Get­System­Time­As­File­Time
    • Query­Interrupt­Time, Query­Unbiased­Interrupt­Time
  • 基于 QPC 和系统时间的 API
    • Get­System­Time­Precise­As­File­Time
    • Query­Interrupt­Time­Precise, Query­Unbiased­Interrupt­Time­Precise

基于系统性能计数器(QPC)的 API

QueryPerformanceCounter,微软文档中把它称之为 QPC。

一般情况下使用的 QueryPerformanceCounter,内核驱动开发者使用的 KeQueryPerformanceCounter 和 .NET 开发者使用的 System.Diagnostics.Stopwatch 都是基于 QPC 的 API。

QPC 是通过计算机上独立运行的高精度硬件计时模块来获得时间戳的。这意味着,使用此 API 获得的时间戳是本机时间戳,不包含任何时区等信息。

由于 QPC 的高精度特性,所以非常适合在单个设备上测量一个小段时间的时间间隔。而这也符合我们本文一开始说到的方法执行耗时测量需求。

QueryPerformanceCounter 得到的值是 Ticks,单位是 100 ns。

1 tick  = 100 ns
1 us = 1000 ns
1 ms = 1000 us
1 s = 1000 ms

基于系统时间的 API

如果你的需求不止是测量获取一个时间间隔,而是需要一个长期保存的时间,或者需要将时间与其他设备进行通信,那么基于单台设备的 QPC 就不符合要求了。

GetSystemTimeAsFileTime 可以用来获取系统时钟时间。这个时间就是基于系统时钟的,所以如果你的时间戳是用来通信的,那么就很有用。当然,如果要在设备之间进行与时间信息相关的同步,还可能需要使用 NTP(Network Time Protocol)先同步时间。

DateTime.Now 获取时间的方法就是这个:

[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern long GetSystemTimeAsFileTime();

这里有一些比较有趣的说法,基于系统时间的 API 也会说成是获取高精度时间,那么跟 QPC 有什么不同呢?

这里我只能拿英文来说话了。来自微软的 Raymond Chen 在它的 The Old New Thing 一书中说,基于系统时间的 API 获取的时间戳精度用的是 “所谓的 Precise”,但实际上应该称之为 “Accurate”,而 QPC 才能称之为实质上的 “Precise”。纠结起来就是 QPC 比基于系统时间的 API 得到的时间戳精度更高。

基于 QPC 和系统时间的 API

Get­System­Time­Precise­As­File­Time 这些 API 既可以获得 QPC 的高精度,又与系统时钟相关,于是你可以使用这些 API 同时获得以上测量的好处。当然,这以性能成本为代价的。


参考资料

.NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)的更多相关文章

  1. 代码中看见一共8个变量参数{SEO,0,0,0,0,0,0,0} 解读!{Top,0,0,0,0,0,0,Top}{Nav,0,0,0,0,0,0,Nav}

    代码中看见{SEO,0,0,0,0,0,0,0}{Top,0,0,0,0,0,0,Top}{Nav,0,0,0,0,0,0,Nav}解读! 举个例子: {GetNew,977,0,23,500,0,0 ...

  2. Java基础学习总结(81)——如何尽可能的减少Java代码中bug

    Java编程语言的人气自然无需质疑,从Web应用到Android应用,这款语言已经被广泛用于开发各类应用及代码中的复杂功能. 不过在编写代码时,bug永远是困扰每一位从业者的头号难题.在今天的文章中, ...

  3. java中异常抛出后代码还会继续执行吗

    今天遇到一个问题,在下面的代码中,当抛出运行时异常后,后面的代码还会执行吗,是否需要在异常后面加上return语句呢? public void add(int index, E element){ i ...

  4. 代码在ie9中不能正确执行

    <!DOCTYPE html> <html> <head lang="zh"> <meta charset="UTF-8&quo ...

  5. Java中普通代码块,构造代码块,静态代码块执行顺序

    //执行顺序:(优先级从高到低.)静态代码块>mian方法>构造代码块>构造方法. 其中静态代码块只执行一次.构造代码块在每次创建对象是都会执行. 1 普通代码块 1 //普通代码块 ...

  6. SQL语句在查询分析器中可以执行,代码中不能执行

    问题:SQL语句在查询分析器中可以执行,代码中不能执行 解答:sql中包含数据库的关键字,将关键字用[]括起来,可以解决. 后记:建数据库的时候尽量避免使用关键字. 例子: sql.Format(&q ...

  7. java中的代码块执行顺序

    /* 代码块:在Java中,使用{}括起来的代码被称为代码块. 根据其位置和声明的不同,可以分为 局部代码块:局部位置,用于限定变量的生命周期. 构造代码块:在类中的成员位置,用{}括起来的代码.每次 ...

  8. 利用Roslyn把C#代码编译到内存中并进行执行

    Tugberk Ugurlu在其博文<Compiling C# Code Into Memory and Executing It with Roslyn>中给大家介绍了一种使用.NET下 ...

  9. jsp页面中的代码执行加载顺序介绍

    1. java是在服务器端运行的代码,jsp在服务器的servlet里运行,而javascript和html都是在浏览器端运行的代码.所以加载执行顺序是是java>jsp>js. 2. j ...

随机推荐

  1. 前端基础(html)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. python开发之路目录

    Python 目录 基础 python入门 python数据类型.字符编码.文件处理 python函数基础 python函数进阶 python装饰器函数 python装饰器函数 python递归函数 ...

  3. (转)WPF学习资源整理

    由于笔者正在学习WPF,所以整理出网络中部分WPF的学习资源,希望对同样在学习WPF的朋友们有所帮助. 首推刘铁猛的<深入浅出WPF>系列博文 1.深入浅出WPF(1)——什么是WPFht ...

  4. Django:学习笔记(8)——文件上传

    Django:学习笔记(8)——文件上传 文件上传前端处理 本模块使用到的前端Ajax库为Axio,其地址为GitHub官网. 关于文件上传 上传文件就是把客户端的文件发送给服务器端. 在常见情况(不 ...

  5. 什么是ASCII码文本文件

    标准ASCII码方式(也称文本方式)存储的文件,更确切地说,英文.数字等字符存储的是ASCII码.文本文件中除了存储文件有效字符信息(包括能用ASCII码字符表示的回车.换行等信息)外,不能存储其他任 ...

  6. oracle中 start with .. connect by prior.. 用法简介

    我们经常会将一个比较复杂的目录树存储到一个表中.或者将一些部门存储到一个表中,而这些部门互相有隶属关系.这个时候你就会用到connect by prior start with.oracle 提供了s ...

  7. source insight 4.0的基本使用方法(转)

    源:source insight 4.0的基本使用方法 source insight 4设置

  8. win10下搭建深度学习--总结【学习笔记】

    win10 下搭建深度学习开发环境总结: 1.本人环境如下:win10,GTX1050TI.i7,anaconda3,vs2015,cuda9.0,cudnn7.1.4,tensorflow-gpu= ...

  9. [问题解决]不使用PWM调速系统,彻底解决一个L298N带动两个电机却转速不同的问题

    问题描述:由单片机的VCC引脚供电,使用L298N控制两个电机,发现左右两个轮子的转速老是不一样,更多的情况是左轮转速高(左轮电机接OUT1和OUT2),右轮转速低(右轮电机接OUT3和OUT4)甚至 ...

  10. js的重载

    1.重载 //重载(个数不同,类型不同)function prop(){var firstP = document.getElementById("p");if(arguments ...