Chinaunix首页 | 论坛 | 博客
  • 博客访问: 705590
  • 博文数量: 107
  • 博客积分: 5079
  • 博客等级: 大校
  • 技术积分: 1330
  • 用 户 组: 普通用户
  • 注册时间: 2006-02-10 10:58
文章分类

全部博文(107)

文章存档

2017年(1)

2015年(8)

2014年(8)

2012年(2)

2011年(7)

2010年(8)

2009年(20)

2008年(53)

分类: LINUX

2011-08-22 14:46:46

文件:rdtsc.rar
大小:32KB
下载:下载


試著把下面ASM語言加到C中, 下面代码红色部分需要你自行修改
                                                                
>;Input:      Stack available                入栈            
>  EAX =   [31]    = X (config.启用)                 ;
>;         [30:24] = 0 (保留)                       ;
>;         [23:16] = Bus Number   总线号                      ;
>;         [15:11] = Device Number   设备号                   ;
>;         [10:8]  = Function Number   功能号                 ;
>;         [7:0]   = Register Number       注册号 


         ;
>        mov    eax, PCI config address (参考上面注解)
>        mov     cx, ax                     ; 保存寄存器
>        mov     dx, 0CF8h               ; 配置地址寄存器
>        or      eax, 080000000h         ; 设置config启用字节
>        and     eax, 0FFFFFFFCh         ;强制DWORD值对齐 
>        out     dx, eax                 ; 写 Bus/Dev/Func/Reg
>        mov     dx, 0CFCh               ; 配置数据寄存器
>        and     cx, 03h                 ; 获得的寄存器的字节偏移
>        add     dx, cx                   ; 添加偏移到  CFCh
>        mov     al, 要写入的数值          ; 移动值写至 AL寄存器
>        out     dx, al                    ;  写入的字节值
>
>例如Bus 00, Device 01, Function 01, register F0,要写入03h
>PCI config address = 000009F0h
>要写入的数值 = 03h


 

例如Bus 00, Device 01, Function 01, register EF,要写入11h

PCI config address = 000009EFh

要写入的数值 = 11h

 
测试只需要 在F0地址写入02使系统正常 写入03使系统变快 EF地址可以不写
 



如果启用了“暂停时钟慢加速(Halt clock ramping)”功能,则当AMD Opteron K8 处理器进入或退出暂停状态时,TSC 会丢失一些周期,即使TSC 频率不变也丢失



Linux中有3种timer:
  1、Real Time Clock(RTC)
  2、Programmalbe Interval Timer(PIT)
  3、Time Stamp Counter.(TSC)

  其中RTC是位于CMOS中的,其频率范围是2HZ--8192HZ.
  PIT主要由8254时钟芯片实现的
  TSC的主体是位于CPU里面的一个64位的TSC寄存器。每个CPU时钟周期其值加一。

  RTC是PC主板上的晶振及相关电路组成的时钟电路的生成脉冲,RTC经过8254电路的频产生一个频率较低一点的OS(系统)时钟TSC,系统时钟每一个cpu周期加一,每次系统时钟在系统初起时通过RTC初始化。8254本身工作也需要有自己的驱动时钟(PIT),可以参考一些单片机方面的书籍。

  按上面的意思,那就是RTC的精确度比TSC更高了。但是查资料发现8254的震荡频率最高是4.194304MHz,也就是说TSC的频率比这个频率更少,而现在的CPU的频率已经超过了GHz,如果每个cpu周期TSC时钟加一,那么TSC的频率岂不可以达到GHz级,这样岂不矛盾了吗?

  另外一些资料说利用TSC可以实现比PIT更精确的定时,也就是不可能由PIT产生了。

  8254本身只是一个定时/计数器,他本身要正常工作需要一个晶振的支持,就好像一个将1mA的电流放大到1A的放大器本身工作也需要一个驱动电流一样,4.194304MHz应该就是你的8254的工作晶振,4.194304MHz不是RTC,RTC是输入给8254的脉冲,经过分频产生os时钟,linux每个系统时钟周期10ms,cpu本身工作(处理指令,数据)也有自己的指令周期,它们是不同的,资料上有些地方说法可能会有点误差。
自从 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 的“钟”会不会再次失步。) 
RDTSC.CPP
#include

unsigned long long native_read_tsc(void)
{
        unsigned long long val;
        asm volatile("rdtsc" : "=A" (val));
        return val;
}

int main()
{
        unsigned long long val;
        unsigned long long val2;

        val = native_read_tsc();
        sleep(1);
        val2 = native_read_tsc();

        printf ("%lld\n", (val2-val)/1000000);

        return (0);

文件:tsc.rar
大小:2KB
下载:下载

阅读(10147) | 评论(1) | 转发(0) |
0

上一篇:6900 GPS

下一篇:Linux下yasm生成ELF

给主人留下些什么吧!~~

chinaunix网友2011-09-05 10:49:17

bash-4.1$ cat t.cpp #include #include #include unsigned char read_pci_config_8(unsigned char bus, unsigned char slot, unsigned char func, unsigned char offset) { unsigned char v; outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); v = inb(0xcfc + (offset&3)); return v; } void write_pci_config_8(unsigned char bus,unsigned char slot, unsigned char func, unsigned char offset, unsigned char val) {