RDTSC指令實現納秒級計時器

2021-09-17 22:07:59 字數 2792 閱讀 9998

原文:

x86 platform

從pentium開始,很多80x86微處理器都引入tsc,乙個用於時間戳計數器的64位的暫存器,它在每個時鐘訊號(clk, clk是微處理器中一條用於接收外部振盪器的時鐘訊號輸入引線)到來時加一。

通過它可以計算cpu的主頻,比如:如果微處理器的主頻是1mhz的話,那麼tsc就會在1秒內增加1000000。除了計算cpu的主頻外,還可以通過tsc來測試微處理器其他處理單元的運算速度,資料[2]介紹了這個內容。

那麼如何獲取tsc的值呢?rdtsc,一條讀取tsc的指令,它把tsc的低32位存放在eax暫存器中,把tsc的高32位存放在edx中,更詳細的描述見資料[1]。

下面來看看rdtsc的具體用法,在linux源**include/asm-i386/msr.h中,可以找到這麼三個關於rdtsc的巨集定義:

#define rdtsc(low,high) \

__asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))

#define rdtscl(low) \

__asm__ __volatile__("rdtsc" : "=a" (low) : : "edx")

#define rdtscll(val) \

__asm__ __volatile__("rdtsc" : "=a" (val))

第三個正是我們需要的,為了方便在我們自己的應用程式中使用,通過資料[3]我們把它簡單的封裝一下:

typedef unsigned long long cycles_t;

inline cycles_t currentcycles()

下面來介紹乙個例項,用rdtsc來計算cpu的主頻。計算方法就是根據tsc的工作原理來的。我們在時間間隔1秒的前後分別記下tsc的值,然後求差並除以1000000。這樣就可以計算出以mhz為單位的主頻了。大概的演算法如下:

t1 = currentcycles();

sleep(1);

t2 = currentcycles();

printf("cpu mhz : %lld\n", (t2-t1)/1000000);

不過考慮到sleep是基於alarm和pause實現的,我們這裡直接通過alarm來產生1秒的時間間隔了。

/**

* readtsc.c -- read the time stamp counter using the rdtsc instruction *

* falcon * 2008-04-07 *

* ref: 1. [url]

* 2. [url] *

*/#include /* printf */

#include /* alarm, pause */

#include #include /* signal,kill */

typedef unsigned long long cycles_t;

inline cycles_t currentcycles()

cycles_t t1, t2;

void handler(int signo)

int main(void)

這裡是執**況:

$ make readtsc

cc readtsc.c -o readtsc

$ ./readtsc

cpu mhz : 2199

$ cat /proc/cpuinfo | grep mhz

cpu mhz : 2200.103

我們算出來的主頻跟核心proc檔案系統中記錄的值差不多,不過核心裡頭計算的要大一些,可能核心在計算主頻時用了浮點運算的緣故。實際上我們計算的值也比真實的要大,因為在t1和t2之間,除了1s外,我們呼叫了alarm,而且進行了除法運算,因此實際時間要大於1秒,所以實際主頻就會更小一些。

參考資料:

[1] rdtsc instruction

[2] using the rdtsc instruction for performance monitoring

[3] use rdtsc instruction to measure time on x86 architecture

end of x86 platform

********* 描述1 ***********

在mips平台上,可以通過讀取coprocessor 0中的暫存器9來獲取time stamp,

#define rdtscl(dest)\

__asm__ __volatile__("mfc0 %0, $9; nop":"=r"(dest));

注:nop 指令是必需的,防止了編譯器在指令mfc0之後立刻訪問目標暫存器。這種互鎖(interlock)在 risc處理器中是很典型的,在延遲期間編譯器仍然可以排程其它指令執行。我們在這裡使用nop,是因為內嵌彙編指令對編譯器來說是個黑盒,不能進行優化。

通過使用這個巨集,mips 處理器就可以執行和前面所示用於 x86 的相同的**了。

gcc 內嵌彙編的有趣之處在於通用暫存器的分配使用是由編譯器完成的。這個巨集中使用的 %0 只是「引數 0」的佔位符,引數 0 由隨後的「作為輸出(=)使用的任意暫存器(r)」定義。該巨集還說明了輸出暫存器要對應於 c 表示式 dest。內嵌彙編的語法功能強大但也比較複雜,特別是在對各暫存器使用有限制的平台上更是如此,如 x86 系列。完整的語法描述在 gcc 文件中提供,一般在 info 中就可找到。

c納秒級計時器 C 11 計時器!真香

在我們寫程式過程中,有時候需要測試我們的程式語句執行時間的耗時,當前也是有很多的庫提供我們去使用,一直沒有良好的跨平台的庫可以提供出來 而且一般這種 也是由我們程式設計師自己呼叫系統的庫來進行,但是往往會出現精度不足和不支援跨平台等問題 他來了。他來了。他踩著七彩祥雲來了 他 就是c 11中引進bo...

高精度納秒計時器

ktimer.h windows graphics programming win32 gdi and directdraw?feng yuan publisher prentice hall ptr first edition december 01,2000 高精度納秒計時器,最後修改 2008...

c語言計時納秒 如何利用rdtsc計算納秒級的時間

從奔騰 penium 系列開始,x86 處理器中增加了乙個64位的時間戳暫存器 tsc 每個經過乙個時鐘週期,該暫存器加1 機器重啟時,該暫存器將清空。現在的處理器其主頻都在1g以上,也就是說其時鐘週期是納秒級的 1秒 1000000000 1納秒 那該暫存器會不會溢位呢?我們可以計算一下,假設機器...