多核时代不宜再用 x86 的 RDTSC 指令测试指令周期和时间

陈硕
Blog.csdn.net/Solstice

自从 Intel Pentium 加入 RDTSC 指令以来,这条指令是 micro-benchmarking
的利器,可以以极小的代价获得高精度的 CPU 时钟周期数(Time Stamp
Counter),不少介绍优化的文章[1]和书籍用它来比较两段代码的快慢。甚至有的代码用 RDTSC 指令来计时,以替换
gettimeofday() 之类的系统调用。在多核时代,RDTSC 指令的准确度大大削弱了,原因有三:

  1. 不能保证同一块主板上每个核的 TSC 是同步的;
  2. CPU 的时钟频率可能变化,例如笔记本电脑的节能功能;
  3. 乱序执行导致 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,就能为各个线程设置亲缘性屏蔽:

  DWORD_PTR 
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); 

 
 
本文对Windows平台下常用的计时函数进行总结,包括精度为秒、毫秒、微秒三种精度的 5种方法。分为在标准C/C++下的二种time()及clock(),标准C/C++所以使用的time()及clock()不仅可以用在 Windows系统,也可以用于Linux系统。在Windows系统下三种,使用Windows提供的API接口timeGetTime()、 GetTickCount()及QueryPerformanceCounter()来完成。文章最后给出了5种计时方法示例代码。

标准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下计时的更多相关文章

  1. QTimer源码分析(以Windows下实现为例)

    QTimer源码分析(以Windows下实现为例) 分类: Qt2011-04-13 21:32 5026人阅读 评论(0) 收藏 举报 windowstimerqtoptimizationcallb ...

  2. windows下实现微秒级的延时

    windowsintegeriostream汇编嵌入式任务 最近正在做一个嵌入式系统,是基于windows ce的,外接硬件的时序要微秒级的延时.1.微秒级的延时肯定不能基于消息(SetTimer函数 ...

  3. windows下多进程加协程并发模式

    好久没更新博客了.正好最近要整理一下最近这段时间做过的项目以及学习python的一些心得.如标题所示,今天就来说说windows下多进程加协程并发模式.其实网上还是蛮多在linux下的多进程加协程并发 ...

  4. 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一)

    相关连接导航 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二) 常用 Gulp 插件汇总 ...

  5. 让 windows 下的命令行程序 cmd.exe 用起来更顺手

    在 Windows 下使用 Larave 框架做开发,从 Composer 到 artisan 总是避免不了和 cmd.exe 打交道,系统默认的命令行界面却是不怎么好看,且每行显示的字符数是做了限制 ...

  6. Windows下Visual studio 2013 编译 Audacity

    编译的Audacity版本为2.1.2,由于实在windows下编译,其源代码可以从Github上取得 git clone https://github.com/audacity/audacity. ...

  7. Windows下Nginx配置SSL实现Https访问(包含证书生成)

    Vincent.李   Windows下Nginx配置SSL实现Https访问(包含证书生成) Windows下Nginx配置SSL实现Https访问(包含证书生成) 首先要说明为什么要实现https ...

  8. 关于Linux和Windows下部署mysql.data.dll的注册问题

    mysql ado.net connector下载地址: http://dev.mysql.com/downloads/connector/net/ 选择版本: Generally Available ...

  9. windows下配置apache+php环境

    PHP安装 由于windows下php扩展5.6的多余7.0,故以php5.6为开发环境.如果对扩展要求不高,可以使用php7,安装过程类似. 约定: 环境安装目录: D:/phpsetup/ |-- ...

随机推荐

  1. RGB与CMYK以及加色与减色

    转载自:http://blog.csdn.net/tohmin/article/details/4761930 黑白色配色与RGB.CMYK 1. 光学三原色与印刷三原色 光学与印刷三原色, 相信大家 ...

  2. [转]【MyBatis】Decimal映射到实体类出现科学计数法问题

    原文地址:https://blog.csdn.net/harwey_it/article/details/80269388 问题: Mybatis查询Decimal字段映射到实体类后,出现科学计数法的 ...

  3. Python3 ctypes简单使用

    >>> from ctypes import * >>> c_int() c_long(0) >>> c_char_p(b'hello') c_c ...

  4. Java heap space cdh 5.11.1

    在执行hive count 查询的时候报错:Error: Java heap space 解决办法是 set io.sort.mb=10; 执行hadoop的Exeample的时候报错,也是java ...

  5. [lsof]lsof查看哪些设备/文件被占用或者打开

    转自:http://blog.csdn.net/yuzhihui_no1/article/details/51767516 最近在查一个Bug,应用程序kill之后重启,总是会出现adc的设备open ...

  6. 处理异常json串的jar包JsonSerde

    参考下面文章: https://blog.csdn.net/SunnyYoona/article/details/70170173

  7. linux系统管理命令(五)

    [教程主题]:1.系统管理命令 [1.1]用户和组管理 在Linux操作系统中,任何文件都归属于某一特定的用户,而任何用户都隶属于至少一个用户组.用户是否有权限对某文件进行访问.读写以及执行,受到系统 ...

  8. logback.xml配置文件详解

    越是老司机越会对日志重视. 如下配置实现了 1.日志文件使用相对路径输出. 2.从日期与文件大小两个纬度分割. 3.特定包与类的日志输出 ** 当存在多个name相同的logger时,逻辑不是很清楚 ...

  9. js学习(一)-对象和函数概念

    //-----------------------js代码-------------------- function class1(){       //类成员的定义及构造函数 this.name = ...

  10. 我对Web开发的认识

    前端 使用mvvm框架,每个视图维护自己的数据模型,更专注于视图模型及状态,在框架的帮助下规范视图与后端的交互及减轻工作量 我的选择是avalon.js 解耦前后端开发 自有资源独立管理,向后端开放资 ...