c++ windows下计时
多核时代不宜再用 x86 的 RDTSC 指令测试指令周期和时间
陈硕
Blog.csdn.net/Solstice
自从 Intel Pentium 加入 RDTSC 指令以来,这条指令是 micro-benchmarking
的利器,可以以极小的代价获得高精度的 CPU 时钟周期数(Time Stamp
Counter),不少介绍优化的文章[1]和书籍用它来比较两段代码的快慢。甚至有的代码用 RDTSC 指令来计时,以替换
gettimeofday() 之类的系统调用。在多核时代,RDTSC 指令的准确度大大削弱了,原因有三:
- 不能保证同一块主板上每个核的 TSC 是同步的;
- CPU 的时钟频率可能变化,例如笔记本电脑的节能功能;
- 乱序执行导致 RDTSC 测得的周期数不准,这个问题从 Pentium Pro 时代就存在。
这些都影响了 RDTSC 的两大用途,micro-benchmarking 和计时。
RDTSC 一般的用法是,先后执行两次,记下两个 64-bit 整数 start 和 end,那么 end-start 代表了这期间 CPU 的时钟周期数。
在多核下,这两次执行可能会在两个 CPU 上发生,而这两个 CPU
的计数器的初值不一定相同(由于完成上电复位的准确时机不同),(有办法同步,见[3]),那么就导致 micro-benchmarking
的结果包含了这个误差,这个误差可正可负,取决于先执行的那块 CPU 的时钟计数器是超前还是落后。
另外,对于计时这个用途,时间 = 周期数 / 频率,由于频率可能会变(比如我的笔记本的 CPU 通常半速运行在
800MHz,繁忙的时候全速运行在 1.6GHz),那么测得的时间也就不准确了。有的新 CPU 的 RDTSC
计数频率是恒定的,那么时钟是准了,那又会导致 micro-benchmarking 的结果不准,见
[2]。还有一个可能是掉电之后恢复(比如休眠),那么 TSC 会清零。 总之,用 RDTSC 来计时是不灵的。
乱序执行这个问题比较简单 [1],但意义深远:在现代 CPU 的复杂架构下,测量几条或几十条指令的耗时是无意义的,因为观测本身会干扰
CPU 的执行(cache, 流水线, 多发射,乱序, 猜测),这听上去有点像量子力学系统了。要么我们以更宏观的指标来标示性能,把"花 xxx
个时钟周期"替换"每秒处理 yyy 条消息"或"消息处理的延时为 zzz 毫秒";要么用专门的 profiler 来减小对观测结果的影响(无论是
callgrind 这种虚拟 CPU,还是 OProfile 这种采样器)。
虽然 RDTSC 废掉了,性能测试用的高精度计时还是有办法的 [2],在 Windows
用 QueryPerformanceCounter 和 QueryPerformanceFrequency,Linux 下用 POSIX
的 clock_gettime 函数,以 CLOCK_MONOTONIC 参数调用。或者按文献 [3] 的办法,先同步 TSC,
再使用它。(我不知道现在最新的 Linux 官方内核是不是内置了这个同步算法。也不清楚校准后的两个 CPU 的“钟”会不会再次失步。)
通过调用SetThreadAffinityMask,就能为各个线程设置亲缘性屏蔽:
SetThreadAffinityMask (
HANDLE hThread,
// handle
to thread
DWORD_PTR dwThreadAffinityMask
// thread
affinity mask
);
该函数中的
hThread 参数用于指明要限制哪个线程,
dwThreadAffinityMask用于指明该线程
能够在哪个CPU上运行。dwThreadAffinityMask必须是进程的亲缘性屏蔽的相应子集。返回值
是线程的前一个亲缘性屏蔽。例如,可能有一个包含4个线程的进程,它们在拥有4个CPU的计算机上运行。如果这些线程中的一个线程正在执行非常重要的操作,而你想增加某个CPU始终可供它使用的可能性,为此你对其他3个线程进行了限制,使它们不能在CPU
0上运行,而只能在CPU
1、2和3上运行。因此,若要将3个线程限制到CPU
1、2和3上去运行,可以这样操作:
//线程0只能在cpu
0上运行
SetThreadAffinityMask(hThread0,0x00000001);
//线程1,2,3只能在cpu
1,2,3上运行
SetThreadAffinityMask(hThread1,0x0000000E);
SetThreadAffinityMask(hThread2,0x0000000E);
SetThreadAffinityMask(hThread3,0x0000000E);
标准C/C++的二个计时函数time()及clock()
time_t time(time_t *timer);
返回以格林尼治时间(GMT)为标准,从1970年1月1日00:00:00到现在的此时此刻所经过的秒数。
time_t实际是个long长整型typedef long time_t;
头文件:#include <time.h>
clock_t clock(void);
返回进程启动到调用函数时所经过的CPU时钟计时单元(clock tick)数,在MSDN中称之为挂钟时间(wal-clock),以毫秒为单位。
clock_t实际是个long长整型typedef long clock_t;
头文件:#include <time.h>
Windows系统API函数
timeGetTime()、GetTickCount()及QueryPerformanceCounter()
DWORD timeGetTime(VOID);
返回系统时间,以毫秒为单位。系统时间是从系统启动到调用函数时所经过的毫秒数。注意,这个值是32位的,会在0到2^32之间循环,约49.71天。
头文件:#include <Mmsystem.h>
引用库:#pragma comment(lib, "Winmm.lib")
DWORD WINAPI GetTickCount(void);
这个函数和timeGetTime()一样也是返回系统时间,以毫秒为单位。
头文件:直接使用#include <windows.h>就可以了。
高精度计时,以微秒为单位(1毫秒=1000微秒)。
先看二个函数的定义
BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
得到高精度计时器的值(如果存在这样的计时器)。
BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
返回硬件支持的高精度计数器的频率(次每秒),返回0表示失败。
再看看LARGE_INTEGER
它其实是一个联合体,可以得到__int64 QuadPart;也可以分别得到低32位DWORD LowPart和高32位的值LONG HighPart。
在使用时,先使用QueryPerformanceFrequency()得到计数器的频率,再计算二次调用QueryPerformanceCounter()所得的计时器值之差,用差去除以频率就得到精确的计时了。
头文件:直接使用#include <windows.h>就可以了。
c++ windows下计时的更多相关文章
- QTimer源码分析(以Windows下实现为例)
QTimer源码分析(以Windows下实现为例) 分类: Qt2011-04-13 21:32 5026人阅读 评论(0) 收藏 举报 windowstimerqtoptimizationcallb ...
- windows下实现微秒级的延时
windowsintegeriostream汇编嵌入式任务 最近正在做一个嵌入式系统,是基于windows ce的,外接硬件的时序要微秒级的延时.1.微秒级的延时肯定不能基于消息(SetTimer函数 ...
- windows下多进程加协程并发模式
好久没更新博客了.正好最近要整理一下最近这段时间做过的项目以及学习python的一些心得.如标题所示,今天就来说说windows下多进程加协程并发模式.其实网上还是蛮多在linux下的多进程加协程并发 ...
- 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一)
相关连接导航 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二) 常用 Gulp 插件汇总 ...
- 让 windows 下的命令行程序 cmd.exe 用起来更顺手
在 Windows 下使用 Larave 框架做开发,从 Composer 到 artisan 总是避免不了和 cmd.exe 打交道,系统默认的命令行界面却是不怎么好看,且每行显示的字符数是做了限制 ...
- Windows下Visual studio 2013 编译 Audacity
编译的Audacity版本为2.1.2,由于实在windows下编译,其源代码可以从Github上取得 git clone https://github.com/audacity/audacity. ...
- Windows下Nginx配置SSL实现Https访问(包含证书生成)
Vincent.李 Windows下Nginx配置SSL实现Https访问(包含证书生成) Windows下Nginx配置SSL实现Https访问(包含证书生成) 首先要说明为什么要实现https ...
- 关于Linux和Windows下部署mysql.data.dll的注册问题
mysql ado.net connector下载地址: http://dev.mysql.com/downloads/connector/net/ 选择版本: Generally Available ...
- windows下配置apache+php环境
PHP安装 由于windows下php扩展5.6的多余7.0,故以php5.6为开发环境.如果对扩展要求不高,可以使用php7,安装过程类似. 约定: 环境安装目录: D:/phpsetup/ |-- ...
随机推荐
- Android开发(一)——全屏或者取消标题栏
先介绍去掉标题栏的方法: 第一种:也一般入门的时候经常使用的一种方法 requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题栏 注意这句一定要写在se ...
- Oracle 自启动配置
最后是放在rc.d/rc.local 里面,否则脚本编写错误 Linux有可能都启不来 [root@ChenJun-CentOS6 bin]# vi /etc/init.d/oracle #!/bi ...
- iOS提示The app icon set named "AppIcon" did not have any applicable content 错误
按照您的错误提示您应该是,这样设置的吧,看下下图: 如果是的话,请确保您将错有的Icon图片都拽进了Images.xcassets这个文件夹中,如下图所示: 如果您的Icon图片并没有在Images. ...
- Mysql注入绕过姿势
1.内联绕过 2.编码绕过,如URLEncode编码,ASCII,HEX,unicode编码绕过 or 1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(9 ...
- golang深度获取子节点
起因 需要在树形结构里获取子树,树形结构一般是存储一维数组,数组元素里存储子节点的指针 代码 package main import ( "errors" "fmt&qu ...
- golang后台任务库
功能需求 本来想用linux自带的corntab,然而corn调试困难,而且不支持以下需求 能分布式 有http接口 能调试 有统计 kala 这个需求也不算小众,于是在网上查了一下,果然有,而且还比 ...
- Android 面试知识集1
今晚在复习Android基础的时候,找到了一些很有价值的基础知识,分享给给位Android的开发者.这些是基础知识,同时也可以当做面试准备.面试题其实是很好的基础知识学习,有空会好好整理相关基础知识. ...
- Maven 解决 下载项目 compiler 为1.5的问题
在 开发Maven 项目的时候,会发现个问题,就是下载下来的项目默认 compiler 为1.5 ,项目报错. 明明之前开发用的是1.7的啊. 这里只需要在pom.xml确定下就好了. <pro ...
- python 文件 IO 操作
Python 的底层操作 * 其实Python的文件IO操作方法,和Linux底层的差不多 打开 f = open(filename , "r") 后面的 "r" ...
- python将dict中的unicode打印成中文
import json a = {u'content': {u'address_detail': {u'province': u'\u5409\u6797\u7701', u'city': u'\u9 ...