多核時代不宜再用 x86 的 RDTSC 指令測試指令週期和時間

多核時代不宜再用 x86 的 RDTSC 指令測試指令週期和時間

陳碩
Blog.csdn.net/Solstice

自從 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 的“鍾”會不會再次失步。)

[1] http://www.ccsl.carleton.ca/~jamuir/rdtscpm1.pdf
[2] http://en.wikipedia.org/wiki/Time_Stamp_Counter

[3] x86: unify/rewrite SMP TSC sync code http://lwn.net/Articles/211051/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章