顯示廣告
隱藏 ✕
※ 本文為 xxxx9659.bbs. 轉寄自 ptt.cc 更新時間: 2013-09-27 11:35:20
看板 C_and_CPP
作者 Schottky (Schottky)
標題 [分享] 精準的計時 function
時間 Wed Sep 25 01:50:31 2013


如我前面說的, 這個問題要看 CPU 和作業系統而定...
CPU 的 rdtsc (rdtscp) 指令讀出來的 counter 直接影響計時精確度,
Windows 作業系統和 UNIX 家族各自有各自的 function call ...

需要使用到精確時鐘的操作大致有以下幾種:
 1. 時鐘, 看目前時間, 或計算兩次看錶之間經過多久
 2. 等待, delay 一段時間不做事
 3. 鬧鐘, 某個時間要醒來做某事

○UNIX:
最準的當然是 POSIX.1-2001 規定的 clock_* 和 timer_* 系列:
clock_gettime(): 取得目前時間
clock_getres(): 取得精確度
clock_nanosleep() 或 nanosleep(): delay, 保證睡過頭
timer_create(), timer_settime() 系列: 鬧鐘
以上計時單位都是 ns, 使用 Sandy Bridge 以後的 CPU , clock_getres() 回傳的
精確度就一律是 1 ns 了,在那之前的 CPU 到底哪些準哪些不準我沒有仔細研究...

不夠準的:

select(): 可以代替 sleep(), 在古代沒有 nanosleep() 而 usleep() 又很兩光地
        busy waiting 時, select() 曾經是精準 sleep 的最佳解。
usleep(): 已過氣, 建議改用 nanosleep()
ualarm(): 可以用 us 為單位指定時間發 SIGALRM
gettimeofday(): 取得目前時間
以上計時單位是 us, 而且你無法取得精確度... 但可以慶幸的是 Linux 本來精確度
就是看 CPU, 所以普通的 PC 精確度 < 1 us 是基本的...

clock(): 老掉牙 function, 單位是 clock, 用 CLOCKS_PER_SEC 可以換算成秒,
        我的 Linux 上 CLOCKS_PER_SEC 是 1000000 也就是說 clock = us
        但印象中以前看過 #define CLOCKS_PER_SEC 1000 的舊開發環境...

time(): 最常見的時間函式, 回傳單位是秒...
        我每次看見有人寫 srand(time(NULL)); 就會抓狂...
        現在科學昌明的 21 世紀有 /dev/urandom 這個東西耶...
alarm(): 令人懷念的鬧鐘, 以秒計費, 不, 以秒計時, SIGALRM 給大家製造不少麻煩
        比如說中斷本來應該 block 住的 system call, 讓它產生 EAGAIN 錯誤...

○Windows:

最準的是 High-Resolution Timer 系列, 其精確度和 CPU 的 rdtsc 一樣準:
QueryPerformanceCounter(): 取得 rdtsc 的 counter 值
QueryPerformanceFrequency(): 取得一秒有幾個 counter, Sandy bridge 以後的
        CPU, 其 frequency 值等於 CPU 時脈 (2.66 GHz CPU, freq = 2672760000)
這個 counter 精確度 < 1 ns
但很遺憾地, Windows 沒有對應的 ns 級 sleep 和 alarm function,
甚至連 us 級都從缺...

不夠準的:
Multimedia Timer: 以前曾經是最準的 timer, 但這個準確度是有代價的,
        當你用 timeBeginPerios(1) 設定精確度為 1 ms 的時候,
        原本 18.2Hz 的 timer chip 會被加速, 導致 timer interrupt 數量暴增,
        然後整個系統效能反而變得很悲劇...
GetTickCount(): 單位是 ms, 但實際精確度無法得知, 通常就是同上...
        另外它每 49.7 天會 overflow 一次, 這曾經造成 Win98 著名的 49.7 天
        當機 bug 以及 Debug 模式的 GetTickCount overflow condition test
GetTickCount64(): 這下再也不會 overflow 了... (鬆一口氣)
clock(): 作用和 Linux 的 clock() 一樣, 只不過 CLOCKS_PER_SEC 是 1000
GetSystemTime(), GetSystemTimeAsFileTime(): 最小單位是 ms
        很兩光的是你沒辦法直接取得 64-bit LARGE_INTEGER 的時間值...
Sleep() 和 SleepEx(): 單位是 ms, 但實際精確度同 Multimedia Timer
SetTimer(): 最常見的鬧鐘, 最小值 10 ms 起跳。

順帶一提 rdtsc 指令並不是絕對神準, 有幾個狀況會出槌,
但這裡並不是組合語言板, 為了避免被水桶還是就此打住。

--
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 220.137.5.19
UncleHS:linux下有perf可以用呀1F 09/25 02:18
Schottky:perf 用途不同吧...2F 09/25 02:24
Bencrie:推整理 XD3F 09/25 09:29
Bencrie:usleep 會 busy waiting ? 不是應該把 CPU 讓出去嗎

感謝指正, 古代許多 UNIX 的 libc 確實是用 busy waiting 在實作, 我記得在
W. Richard Stevens 的 UNIX Network Programming 有提到。由於 usleep 是個
library function, 不是 system call, 所以要看各家 C library 不同實作方式。

剛剛看了 GNU libc 2.18 的 source code, 在有 nanosleep() system call 的
Linux 上是用 nanosleep 實作, 在沒有 nanosleep() 的古代 BSD 上是用 select()
實作, 所以使用 glibc2 的系統應該是不會有 busy waiting 問題。

Linux 的 nanosleep() 是 kernel 裡的 system call, 以前曾經發生過在某個 mode
之下為了種種原因還是用 busy waiting 來實作, 不過 2.6 和 3.x 之後的 kernel
已經修正, 不用再擔心了。

CaptainH:5F 09/25 09:40
purincess:有DVFS的CPU用rtdsc會爆炸的..XD6F 09/25 12:26

DVFS (CPU 變頻) 這功能被罵翻了 XD
※ 編輯: Schottky        來自: 220.137.37.104       (09/25 13:03)
Bencrie:喔喔 感謝說明 XD7F 09/25 13:09
user1120:推!8F 09/25 14:47
licheer:Sleep()不準9F 09/25 15:12
Schottky:Sleep()是不準,不過Windows平台有其他sleep可選擇嗎?10F 09/25 19:15
Schottky:也只能睡飽了再用QueryPerformanceCounter()估計誤差了
lc85301:可是用/dev/urandom起始srand比較麻煩OAO12F 09/25 22:44
lc85301:還是直接用urandom取代srand
Schottky:不在乎極速不夠高的話 urandom 就直接用了(有些人會在意)14F 09/25 23:01
Schottky:rand()和srand()也是個歷史遺毒--取亂數幹嘛還要播種
Schottky:像PHP那樣第一次用rand()時自動seed不就很方便嗎?
Schottky:urandom的演算法用SHA,比大部份C lib的rand用LFSR好多了
Schottky:LFSR只是統計上的特性合標準,不可預測性是很爛的
tomnelson:唉~ 以前曾經遇過49.7天的bug作祟19F 09/25 23:34
realmeat:詳細推21F 09/26 19:34
licheer:可以掛RTOS來用22F 09/26 20:01

--
※ 看板: xxxx9659 文章推薦值: 0 目前人氣: 0 累積人氣: 176 
分享網址: 複製 已複製
guest
x)推文 r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇