Windows上的类似clock_gettime(CLOCK_MONOTONIC)的高精度测量时间函数

2024年4月11更新
感谢评论提醒,我之前写《如何在C/C++中测量一个函数或者功能的运行时间(串行和并行,以及三种方法的实际情况对比)》的时候只实验了 Linux 和 Mac 这种类 Unix 系统,没考虑到 Windows。

本文只考虑第一方(微软)的时间测量功能,有一些第三方高精度测量时间的库这里不讨论。

Windows 中高精度测量时间间隔最佳方法是 QPC(QueryPerformanceCounter,查询性能计数器)。

QPC 不依赖于外部时间参考,是一个差动时钟(Difference Clocks),而不是一般我们常说的绝对时间(例如“2020/3/18 14:29:59”,有时也被很形象地称为墙上时间)类似clock()。并且 QPC 并不会受标准时间、系统时间的影响,类似clock_gettime()中的CLOCK_MONOTONIC

QPC 使用硬件计数器来计算时间。
一般在 x86 架构设备上, QPC 来测量时间是通过访问处理器的的 TSC(时间戳计数器)来实现的,不过某些设备的 BIOS 可能不能正确设置 CPU 特性,比如设置成可变 TSC,那这个就会受其他一些因素的影响了。或者拥有多个处理器的设备,因为这样就有两个 TSC 来源了,他们俩不一定一样。如果出现这种情况,那么 Windows 会使用平台计数器或者主板上其他的计时器,而不是 TSC。这样的话成本会高 0.8~1.0 微秒。
虽然主要是使用 TSC 实现的,但是微软官方不建议使用使用 RDTSC/RDTSCP(后者多一个指定 CPU)来直接获取 TSC 信息,因为这样软件程序的兼容性会大大降低(比如说程序运行在可变 TSC 或者没有 TSC 的设备系统上,代码可能无法运行或者误差较大)。
一般 C/C++ 编译器是有内置函数__builtin_ia32_rdtsc()__builtin_ia32_rdtscp(),所以你可以直接使用uint64_t rdtsc = rdtsc();这句代码来获取计数器的计数,然后做差,类似clock()的使用方法,不过你还要计算一下 TSC 频率来获取准确时间。
TSC 只是其中之一,Windows 8 及之后版本的 Windows 会使用多个硬件计数器来检测误差,并尽量补偿。

但是 QPC 精度比clock_gettime()方法低两个数量级,只能达到 100 纳秒,做不到clock_gettime()的 1 纳秒精度,不过大多情况都足够使用了。

下面是 QPC 的一个例子(第一行是为说明需要导入哪个库):

#include <windows.h>int main()
{LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;LARGE_INTEGER Frequency;QueryPerformanceFrequency(&Frequency);QueryPerformanceCounter(&StartingTime);...需要被测量的代码QueryPerformanceCounter(&EndingTime);printf(" %.1f us", 1000000*((double)EndingTime.QuadPart - StartingTime.QuadPart)/ Frequency.QuadPart);   
}

LARGE_INTEGER是 Windows 上的一个 union,它的内容如下:

typedef union _LARGE_INTEGER {struct {DWORD LowPart;LONG  HighPart;} DUMMYSTRUCTNAME;struct {DWORD LowPart;LONG  HighPart;} u;LONGLONG QuadPart;
} LARGE_INTEGER;

它是 Windows 上用来存放 64 位整数的一个数据类型。如果编译器内置了对 64 位整数的支持,请使用 QuadPart 成员来存储 64 位整数。否则,请使用 LowPart 和 HighPart 成员来存储 64 位整数。可以看到上面例子中是使用QuadPart成员变量来读取一个 64 位的整数。

如果你对 union 不熟悉,请看我的另外一篇博客:C——Union是什么?Union和Struct这么像,区别在哪?为什么还要创造出union呢?需要在哪里使用呢?

QueryPerformanceFrequency是获取计数器频率。前文也提到过, QPC 是通过 TSC 之类的硬件计数器实现的,所以需要知道晶振的频率,通过计数/频率来计算出时间。

QueryPerformanceCounter用来获取当前计数值。

printf(" %.1f us", 1000000*((double)EndingTime.QuadPart - StartingTime.QuadPart)/ Frequency.QuadPart);就是用来打印计算出的时间。((double)EndingTime.QuadPart - StartingTime.QuadPart)/ Frequency.QuadPart就是计数/频率这个公式了。前面的1000000用来转换单位,表示微秒。如果你要计算毫秒就是1000,纳秒就是1000000000

需要注意针对不同单位使用不同的%.xf。前面提到的精度只有 100 纳秒,如果你使用1000000000,以纳秒为单位,会发现整数最右边两位永远是0,如下:

请添加图片描述

所以打印微秒时使用%.1f,纳秒时使用%.f%.0f即可。再多的位数就超出精度范围了(当然或许会有对齐等情况的需要,所以还是看自己,这只是一个建议)。

希望能帮到有需要的人~

参考资料/扩展阅读

Acquiring high-resolution time stamps - Microsoft Learn:这篇是微软官方关于获取高精度时间的文章,如果你想了解一些关于 Windows 如何获取时间的底层和其他知识可以看看。我觉得最值得看的就是关于误差的那部分Resolution, Precision, Accuracy, and Stability,介绍了通过硬件计数器获取时间的时候会造成误差的一些原因,是一个不错的扩展。

Time - Microsoft Learn:介绍 Windows 上各种时间的专栏。

LARGE_INTEGER union (winnt.h) - Microsoft Learn:LARGE_INTEGER的介绍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/620272.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

总结java中的synchronized锁

目录 synchronized的特性 synchronized的锁机制 synchronized的使用 synchronized的特性 synchronized主要有三大特性&#xff1a; 面试时经常拿synchronized关键字和volatile关键字的特性进行对比&#xff0c;synchronized关键字可以保证并发编程的三大特性&#xff1a;原子…

yolo系列(之一)

深度学习经典检测算法 two-stage (两阶段) : Faster-rcnn Mask-Rcnn系列 &#xff08;输入图像---》CNN特征---》预选框---》输出结果&#xff09; one-stage (单阶段): YOLO系列 &#xff08;输入图像---》CNN特征---》输出结果&#xff09; one-stage的特点&#xff1a;&…

python计算

优先级&#xff1a;小括号&#xff08;&#xff09;>幂运算&#xff08;指数&#xff09;>正负号>算术运算&#xff08;先乘除后加减&#xff09;>比较运算>逻辑运算

网络基础(二)——传输层

1、再谈端口号 端口号(Port)标识了一个主机上进行通信的不同的应用程序; 在TCP/IP协议中, 用 "源IP", "源端口号", "目的IP", "目的端口号", "协议号" 这样一个五元组来标识一个通信(可以通过 netstat -n查看); 1.1、端口号…

【详细讲解下Photoshop】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

第十五届蓝桥杯c++b组赛后复盘和真题展示

题目变成八道了&#xff0c;分数一百分可能&#xff0c;感觉拿奖难度还是很高 第一题是一个简单的握手问题 答案算出来1204&#xff0c;纯手写 第二题是 物理题 纯蒙&#xff0c;随便猜了个轨迹&#xff0c;答案具体忘了&#xff0c;最后是 .45 第三题暴力 第四题 我是傻逼…

jenkins 宝塔部署及集成到码云自动构建代码

jenkins 宝塔部署及集成到码云自动构建代码 ps:本文所有涉及软件包一键下载 一、Jenkins包下载 大家可以从Jenkins官网(https://www.jenkins.io/)根据自己的需要下载最新的版本。 但Jenkins官网下载较慢,容易造成下载失败。可以去国内的开源镜像网站下载Jenkins最新版本。…

揭秘!这些部门对六西格玛技术情有独钟

当下&#xff0c;企业为了提升产品质量、降低成本、提高效率&#xff0c;纷纷寻求先进的管理方法和技术手段。其中&#xff0c;六西格玛技术因其卓越的绩效改善能力&#xff0c;受到了众多企业的青睐。那么&#xff0c;哪些部门对六西格玛技术情有独钟呢&#xff1f;天行健六西…

城市建筑渣土车智慧管理,让城市更智能!

在这个充满高楼林立的城市&#xff0c;建筑业已经成为了城市发展的重要支柱。而随之而来的&#xff0c;就是大量的建筑渣土需要清运。在这个过程中&#xff0c;渣土车成为了不可或缺的角色。 然而&#xff0c;渣土车管理却成为了一个难题。由于管理不善&#xff0c;很多渣土车不…

消息队列RabbitMQ入门学习

目录 1.初识MQ 1.1.同步调用 1.2.异步调用 1.3.技术选型 2.RabbitMQ 2.1.收发消息 2.1.1.交换机 2.1.2.队列 2.1.3.绑定关系 2.1.4.发送消息 3.SpringAMQP 3.1WorkQueues模型 3.1.1消息接收 3.1.2测试 3.1.3.能者多劳 3.1.3.总结 3.2.交换机类型 3.3.Fanout交…

JavaScript-2.对话框、函数、数组、Date、DOM

对话框 window对象封装了三个对话框用于与用户交互 提示框&#xff1a;alert(title);确认框&#xff1a;confirm(title);输入框&#xff1a;prompt(title); 确认框 包含两个按钮“确认”/“取消”&#xff0c;点击确定时&#xff0c;返回值为true // 确认框 var bool con…

5.Godot节点和功能及Node节点属性分析

1. 节点和功能的关系 节点 Node &#xff0c;用于实现一种功能&#xff0c;例如&#xff0c;Sprite 节点&#xff0c;用于图片的显示一个节点的功能取决于它挂载了哪些子节点&#xff0c;它包含了哪些功能的子节点&#xff0c;就包含了对应子节点表示的功能节点是可选的&#…