看板 C_and_CPP 關於我們 聯絡資訊
如我前面說的, 這個問題要看 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可以用呀 09/25 02:18
Schottky:perf 用途不同吧... 09/25 02:24
Bencrie:推整理 XD 09/25 09:29
Bencrie:usleep 會 busy waiting ? 不是應該把 CPU 讓出去嗎 09/25 09:30
感謝指正, 古代許多 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: 09/25 09:40
purincess:有DVFS的CPU用rtdsc會爆炸的..XD 09/25 12:26
DVFS (CPU 變頻) 這功能被罵翻了 XD ※ 編輯: Schottky 來自: 220.137.37.104 (09/25 13:03)
Bencrie:喔喔 感謝說明 XD 09/25 13:09
user1120:推! 09/25 14:47
licheer:Sleep()不準 09/25 15:12
Schottky:Sleep()是不準,不過Windows平台有其他sleep可選擇嗎? 09/25 19:15
Schottky:也只能睡飽了再用QueryPerformanceCounter()估計誤差了 09/25 19:29
lc85301:可是用/dev/urandom起始srand比較麻煩OAO 09/25 22:44
lc85301:還是直接用urandom取代srand 09/25 22:45
Schottky:不在乎極速不夠高的話 urandom 就直接用了(有些人會在意) 09/25 23:01
Schottky:rand()和srand()也是個歷史遺毒--取亂數幹嘛還要播種 09/25 23:02
Schottky:像PHP那樣第一次用rand()時自動seed不就很方便嗎? 09/25 23:03
Schottky:urandom的演算法用SHA,比大部份C lib的rand用LFSR好多了 09/25 23:04
Schottky:LFSR只是統計上的特性合標準,不可預測性是很爛的 09/25 23:22
tomnelson:唉~ 以前曾經遇過49.7天的bug作祟 09/25 23:34
realmeat:詳細推 09/26 19:34
licheer:可以掛RTOS來用 09/26 20:01
james732: 推詳細 05/21 17:45
os653: 取亂數需要播種這玩意兒在寫replay的時候還是很有用的 05/22 18:44