编程获得CPU的主频
CPU的主频,即CPU内核工作的时钟频率(CPU Clock Speed)。CPU的主频表示在CPU内数字脉冲信号震荡的速度。主频和实际的运算速度存在一定的关系,但目前还没有一个确定的公式能够定量两者的数值关系,因为CPU的运算速度还要看CPU的流水线的各方面的性能指标(缓存、指令集,CPU的位数等等)。由于主频并不直接代表运算速度,所以在一定情况下,很可能会出现主频较高的CPU实际运算速度较低的现象。
在windows操作系统中,可以使用右键点击“我的电脑”,查看属性来获取CPU的主频信息,然而该信息是存在于注册表之中的。也就是说可以通过修改注册表来伪造CPU主频信息。那能不能用其它方法获得该信息呢?
检测CPU的速度,一般是测试在单位时间内运算的指令条数,但用这种方法有太大的局限性,由于受到很多因素的影响,准确度比较低,因为你不知道在你的程序外别的程序占用了多少的时间片。其实在586及之后处理器中,已经有了一条专用的指令来测试主频,那就是 RDTSC指令,意思是读取时间标记计数器(Read Time-Stamp Counter),Time-stamp counter 是处理器内部的一个64位的MSR (model specific register),处理器每时钟周期递增时间标签计数器 MSR 一次,在处理器复位时将它重设为 0。RDTSC 指令把 TSC的值低32位装入EAX中,高32位装入EDX中。如果CPU的主频是200MHz,那么在一秒钟内,TSC的值增加 200,000,000 次。所以在计算的时候,把两次的TSC差值除以两次的时间差值就是CPU的主频。
从上面的资料得到了一个思路:首先使用RDTSC指令获取1个TSC的值,将其存储起来,再延时1秒,使用RDTSC指令获取1个新的TSC值,并用其减去第一次获得的TSC值,即可得到该CPU的主频。用C和汇编混合的代码如下:
#include <windows.h>
#include <stdio.h>
int main(int argc,char* argv[])
{
static int time[2];
int quotient = 0; //商
int remainder = 0; //余数
__asm{
rdtsc // read time-stamp count
mov ebx,offset time //将time的偏移地址存入ebx
mov [ebx+0],edx //把TSC的值的高32位存入[ebx+0]中
mov [ebx+4],eax //把TSC的值的低32位存入[ebx+4]中
}
Sleep(1000);
__asm{
rdtsc // read time-stamp count
mov ecx,offset time //将time的偏移地址存入ecx
sub eax,[ecx+4] //把延时1秒后的TSC值的低32位减去1秒前的TSC值的低32位
sbb edx,[ecx+0] //把延时1秒后的TSC值的高32位减去1秒前的TSC值的高32位
mov ecx,1000000000 //转换成GHz
div ecx
mov quotient,eax //将结果中的商赋值于quotiend
mov remainder,edx //将结果中的余数赋值于remainder
}
remainder = remainder / 10000000; //余数仅保留两位
printf("该机主频为:%d.%d",quotient,remainder);
return 0;
}
注意:寄存器 CR4 中的时间标签禁用 (TSD) 标志限制 RDTSC 的使用。清除 TSD 标志时,RDTSC 指令可以在任何特权级别执行;设置此标志时,指令只能在特权级别 0 执行。在特权级别 0 执行时,时间标签计数器还可以使用 RDMSR 指令读取。
但是在多核时代,RDTSC 指令的准确度大大削弱了,原因有如下几点:
1. 不能保证同一块主板上每个核的 CPU 时钟周期数(Time Stamp Counter)是同步的;
2. CPU 的时钟频率可能变化,例如笔记本电脑的节能功能;
3. 乱序执行导致 RDTSC 测得的周期数不准。 虽然 RDTSC 废掉了,高精度计时还是有办法的,在 Windows 上用 QueryPerformanceCounter 和 QueryPerformanceFrequency,Linux 上用 POSIX 的 clock_gettime 函数,以 CLOCK_MONOTONIC 参数调用。
在接下来的几篇文章章中,我会继续介绍相关内容。
参考文章:
1、多核时代不宜再用 x86 的 RDTSC 指令测试指令周期和时间
http://blog.csdn.net/solstice/article/details/5196544
2、RDTSC命令详解
http://blog.csdn.net/tbwood/article/details/5536597
- 如何在三年内快速成长为一名技术专家
- 【微信开发】getAccessToken 和 getJsapi_ticket缓存支持
- 微信JS-SDK 注入权限验证配置
- Maven 项目下slf4j 包冲突问题
- 谷歌教你学 AI-第三讲简单易懂的估算器
- 黑产是如何强刷用户银行卡8.1万元的?
- Spring 必知概念(二)
- 利用iText 组件导出PDF
- 利用POI组件导出Excel的简单示例
- MongoDB 整合spring-data-jpa,spring 4.x
- 【微信开发】 红包接口开发
- 【微信开发】 使用单例设计模式 提供AccessToken 和Jsapi_ticket缓存支持
- @ResponseBody响应JSON 406
- tomcat不能运行或共存多个项目
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- AngularJS 中的 controllerAs
- Android-Jetpack笔记-DataBinding
- Android-Jetpack笔记-Lifecycles
- Android-Jetpack笔记-LiveData
- Android-Jetpack笔记-ViewModelSavedState
- Android-Jetpack笔记-ViewModel
- GitLab CE 安装记录
- SparkSQL中产生笛卡尔积的几种典型场景以及处理策略
- TypeScript 中的 export 和 import
- Angular2 初体验
- 在 Ubuntu 系统上配置 Nginx Git 服务器
- 设计模式之规格模式
- Android-Jetpack笔记-Navigation之Fragment使用
- Android-Jetpack笔记-Navigation之Fragment支持复用
- Android-Jetpack笔记-Room