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

如果你希望在 .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. 通过.properties配置文件,在Service层获取值

    问题:从配置文件获取不到值的原因:1.静态变量:2.没通过Spring加载该实例对象. 1. conf.properties配置文件内容: 2. Spring加载配置文件内容,spring-confi ...

  2. 几种Memcache的状态监控的工具,以及安装和使用【linux系统】

    1.Memcache-top的简介及安装和用法 简介:memcache-top是用perl语言编写的,可以运行在term下.它能够像top一样显示各个memcached节点的状态变化,其中包括系统管理 ...

  3. 一个简单清晰的Redis操作类-php

    <?php /** * redis处理的二次封装 * */ class Redis{ private $_redis; private $_config; public function __c ...

  4. 20145326 《Java程序设计》第5周学习总结

    20145326 <Java程序设计>第5周学习总结 教材学习内容总结 第八章 一.语法与继承结构 1.使用try .catch 我们编写程序时总有些由意想不到的状况而引发的错误,java ...

  5. Linux网络子系统之---- PHY 配置

    MII即媒体独立接口,也叫介质无关接口. 它包括一个数据接口,以及一个MAC和PHY之间的管理接口(图1). 数据接口包括分别用于发送器和接收器的两条独立信道.每条信道都有自己的数据.时钟和控制信号. ...

  6. 【bzoj2721】[Violet 5]樱花

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2721 好久没做数学题了,感觉有些思想僵化,走火入魔了. 这道题就是求方程$ \frac ...

  7. Tensorflow1.5.0+cuda9.0+cudnn7.0+gtx1080+ubuntu16.04

    目录 Tensorflow1.5.0+cuda9.0+cudnn7.0+gtx1080+ubuntu16.04 0. 前记 1. 环境说明 2. 安装GTX1080显卡驱动 3. CUDA 9.0安装 ...

  8. 16s workfollw

    http://bioconductor.org/packages/devel/bioc/vignettes/metagenomeFeatures/inst/doc/Example_16S_Annota ...

  9. python 正则匹配字符串里面的字符

    import re x=re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest') print(x)

  10. iframe与父页面传值

    最近做的项目中用到了不少iframe,而且需要实现: 从父页面获取iframe中某个元素的值或则从iframe页面获取其父页面上某个元素的值. 在网上查询了相关东西,后总结如下: (1)父页面获取if ...