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/ |-- ...
随机推荐
- js设置cookie(原生js)
cookie 与 session 是网页开发中常用的信息存储方式.Cookie是在客户端开辟的一块可存储用户信息的地方:Session是在服务器内存中开辟的一块存储用户信息的地方. JavaScrip ...
- JAVA-JSP内置对象之request获得参数的所有参数值(多个值)
相关资料:<21天学通Java Web开发> 获得参数的所有参数值(多个值)1.需要使用request对象的getParameterValues()方法. RequestForm4.jsp ...
- MYSQL优化9大法!
通常网站的性能瓶颈在数据库查询,如果你希望你的网站在一定阶段之内保持稳定,优化你的SQL和数据库是非常必要的一个优化环节.优化数据库是一个很大的话题,这里只是摘要一些比较关键的优化参考建议,并且需要具 ...
- <花荣《至尊狐狸》中国股市精英最优套利战术>读书笔记
书在这里 第一定律:博弈分析是一切实战操作的基础,资金的机会利润由主力投机状态决定,资金的风险承受度由投资标的价值底线锁定 第二定律:套利战术应是背景性的.主动性的.互动性的,是最安全最保守的.最明确 ...
- C++客户端访问Java服务端发布的SOAP模式的WebService接口
gSOAP是一个绑定SOAP/XML到C/C++语言的工具,使用它可以 简单快速地开发出SOAP/XML的服务器端和客户端 Step1 使用gsoap-2.8\gsoap\bin\win32\wsdl ...
- Linxu安装Tomcat与Jdk并卸载自带OpenJdk
下载安装tomcat 1.下载apache-tomcat-7.0.47.tar.gz tomcat压缩包上传到linxu服务器 2.cd到文件存放目录使用(tar -xvzf apache-tomca ...
- Eclipse 下安装 SVN的方法
http://welcome66.iteye.com/blog/1845176 eclipse里安装SVN插件,一般来说,有两种方式: 直接下载SVN插件,将其解压到eclipse的对应目录里 使用e ...
- FutureTask 源码分析
FutureTask 源码分析,这个类的原理与我分析android当中的FutureTask类差不多[http://www.cnblogs.com/daxin/p/3802392.html] publ ...
- C#获取MySql 数据常用的代码
1.读取 public DataTable ExecuteDataTable(string SQLString) { using (MySqlConnection connection = new M ...
- Matlab基本用法
转至:http://blog.sina.com.cn/s/blog_8354dda801012dyn.html 目录: 一.说明 二.数据类型及基本输入输出 三.流程控制 四.循环 五.数组.数组运算 ...