標籤:
原著:Johan Nilsson
翻譯:lxhui
原文出處:MSDN Magazine March 2004(Timers...)
原代碼下載: HighResolutionTimer.exe (404KB)
本篇文章假定你熟悉 C++ 和 Win32 API
概要 從 Windows NT 裡獲得的時間戳記(Timestamp),根據你所使用的硬體,其最大精度為 10 到 15 毫秒。但是, 有時候你需要時間標籤頻繁事件時,獲得更高的精度更能令人滿意。舉個例子,如果你要與線程打交道,或者以間隔不低於 10 毫秒的頻率實現某些其它任務時該怎麼辦?為了獲得更好的精度,建議的方法包括使用效能計數器和系統時間一起去計算更小的時間增量。然而使用效能計數器技術有其自身的問題。本文將揭示一種可行的途徑來克服該方法固有的局限。
你為什麼會對獲得小於1毫秒精度的系統時間感興趣?在我工作期間,我發現有必要去確定我的進程裡不同線程執行引發的事件的順序。還需要把這些事件同絕對時間相 關聯,但注意到系統時間的實際精度是不會超過10毫秒粒度的。 在本文隨後的內容中,我將解釋該系統時間精度的限制,解決的步驟,以及某些一般缺陷。例子程式的實現可以從本文開始連結處下載。這些檔案的原始碼是在 Visual C++? 7.1 和 Windows? XP 專業版下編寫測試的。在編寫本文時,我頻繁地提到 Windows NT® 作業系統家族(Windows NT 4.0, Windows 2000, 或者 Windows XP)產品,而不是某一個特定的版本。 本文中用到的 Win32? APIs 的參數類型及用法,參見 MSDN library/Platform SDK 文檔。
究竟誰有這樣的需求? 最近我用“Windows NT millisecond time resolution”作為關鍵字在 Internet 上搜尋了一番, 得到了 400 多個滿足條件的結果。其中大多數是討論如何獲得高於10毫秒精度的系統時間,或者是如何讓一個線程的休眠時間小於10毫秒。本文我將專註於為什麼獲得一個高於10毫秒精度的系統時間 會如此困難。你可能認為用 GetSystemTime API 很容易解決問題,這個 API 函數返回一個SYSTEMTIME 結構,該結構包含一個 wMilliseconds 域,在 MSDN 文檔中說它儲存 當前的毫秒時間。但實際上並不象這麼簡單。那麼用 GetSystemTimeAsFileTime 擷取 100 納秒的精度如何呢?就讓我們從一個小實驗 開始吧:嘗試重複擷取系統時間,將它格式化並輸出到螢幕上(見 Figure 1 )。 我的目標不是納秒,而僅是毫秒精度,它應該能夠從 SYSTEMTIME 結構中判斷。讓我們看一下輸出結果:20:12:23.47920:12:23.47920:12:23.49420:12:23.494[...有很多被移去了...]20:12:23.49420:12:23.50920:12:23.50920:12:23.509... 正如你所看到的,我所能得到的最好的精度是15毫秒,這是 Windows NT 刻度的長度。每過一個刻度,Windows NT都會更新系統時間。Windows NT調度器也會 突然啟動並可能選擇一個新的線程來執行。關於這方面的更多資訊,請看《Inside Windows 2000》第三版(Microsoft Press®, 2000),作者是 David Solomon 和 Mark Russinovich。 如果你運行我剛才所示的代碼,你也許會看到時間大約是每10毫秒更新一次。如果是那樣,可能意味著你是在單一處理器的機器上運行 Windows NT,其刻度通常為10毫秒。正如你所看到的, 在這種方法中,系統時間更新頻率不夠快,不足以成為一種為我所用的技術。下面我們就來嘗試找一個解決方案。
最初的嘗試 當你詢問如何得到一個比10毫秒精度更好的系統時間時,你也許會得到下面這樣的回答:使用效能計數器,並讓效能計數器值和即時變化的系統時間同步。結合這些值來計算一個 精度極高的目前時間。Figure 2 顯示了實現方法。 效能計數器是一個高精度的硬體計數器,它能高精確、低開銷地計量一個短周期時間。我通過在一個緊湊迴圈內不斷重複把效能計數器值和對應的系統時間進行同步,等待系統時間變化。當系統時間 以變,我就儲存計數器的值及系統時間。 使用這兩個值作為參考,就有可能計算出一個高精度的當前系統時間(詳情見 Figure 2 中的get_time),看一下結果:...21:23:22.29621:23:22.29721:23:22.29721:23:22.29821:23:22.29821:23:22.29921:23:22.30021:23:22.30021:23:22.30121:23:22.30121:23:22.30221:23:22.30221:23:22.303... 儘管它看起來非常成功,但這個實現卻有幾個問題:同步實現(函數被命名為 "simplistic_synchronize"的一個很好的理由);QueryPerformanceFrequency 報告的頻率 ;系統時間變化缺乏保護。在接下來的章節中,我們會考慮這些問題的一些可能的改進。
實現同步的可靠方法 該同步實現沒有考慮 Windows NT 調度器的搶先問題。例如,它無法保證在下面的兩行代碼之間不會發生線程內容相關的切換,從而導致一個未知時間周期的延遲:::GetSystemTimeAsFileTime(&ft1);::QueryPerformanceCounter(&li); 大多時候只要滿足下面的條件,這個過分單純化的同步函數還是成功的:
- 當前線程不會被優先順序更高的線程搶先進入就緒狀態;
- 當前線程的時間片永遠不會結束
- 很少有硬體中斷(不同於時鐘中斷自身)
為此,最簡明的解決方案是將進程的優先權提升為 REALTIME_PRIORITY_CLASS,將線程的優先權提升為 THREAD_PRIORITY_TIME_CRITICAL,從而阻止在同步期間線程被搶先。不幸的是,對於硬體中斷你沒有什麼可做的,但行為良好的驅動程式應該處理它們的中斷,排隊延期的程序呼叫(deferred procedure call, DPC),甚至以微秒級次序處理DPC。問題是你不能保證系統內所有驅動程式的行為都良好。事實上,即使在你系統裡只有乖巧聽話的驅動程式,你仍然會有許多中斷。 儘管如此,我們還是有一個可靠的同步方法,不必提升進程和線程的優先順序。Figure 3 是這個方法實現步驟的基本流程圖。 Figure 3 可靠的同步
你需要不斷地檢查看系統時間是否變化,就像 Figure 2 所示的 simplistic_synchronize 實現一樣。同先前實現的最大不同之處是你現在也要用效能計數器本身去驗證你始終保持於希望的精確層級。這聽起來很簡單,但仔細看看 Figure 3 便會看出它並不像想象的那麼簡單。需要進一步的解釋,比如你為什麼在 prev_diff 變數中儲存效能計數器最近兩個值之間的差異。原因是從系統時間被儲存到t1的點到計數器值被儲存到p1的點,系統時間可能會有潛在的變化而沒有被檢測到,直到下一次內部迴圈執行(才能檢測到系統時間變化)。 接下來,你可能錯誤地假設在最新的兩個計數器值之間(註:P1->P0)時間變化了,而實際上卻沒有。為了對此進行安全保證,你應該假定系統時間變化要麼在最新的兩個計數器值(註:即prev_diff)之間;要麼在先前的兩個計數器值之間(除了在迴圈內部發生了不可能的事件——該事件通過內部迴圈改變了最開始的時間)。在同步末尾,實現一個計數器值的調整;這可以保證傳回值能夠在希望的精度之內。Figure 4 顯示了這個過程。
Figure 4 計算
這個同步方法需要多次迭代完成,但實際上還不能證明有問題。有關同步的更多資訊及其精確性,你應該看一下本文副題 “同步:有多好?”。
頻率問題 儘管我們已經有了一個好的開端,仍有些問題需要解決。假設你及時在某些特定環節執行這個同步操作。然後,無論何時,只要你需要高精度的時間,就調用get_time。如早先講述,QueryPerfomanceCounter 報告的頻率被用於以高精度計算當前系統時間。由 get_time 報告的時間一定會同實際的時鐘時間發生很大偏差的,得到一個比你所獲得的精度大得多的值。這是因為效能計數器天生不是被用來計量長周期時間的。 我進行了一個小測試考察這個影響會有多大,以 2 微秒作為可接受的同步極限。(我選擇 2 微秒是因為我的 雙 PII 400HZ CPU 機子能得到最好結果),Figure 5 顯示了這個結果。
Figure 5 同步測試
測試結果證明,僅在 110 秒後我的高精度時鐘就偏離了實際系統時間1毫秒。速算一下表明效能計數器頻率報告中存在一個大約百萬分之九 的錯誤。一個0.000009的錯誤聽起來很微小,但是如果你想報告一個微秒以下的時間這就是一個很大的衝擊了。起初,我有兩個想法。第一,使用者負責週期性再同步,而且由此必須決定多長時間做一次。第二,同步由一個後台線程每n秒執行一次。 進行第一個想法的測試之前,我就決定反對它了。第二個想法似乎更加可行,我所能預見的唯一問題就是在用戶端和同步線程之間的必須的同步會產生一些開銷。使用上的簡單總是會增加複雜性和開銷的。 本文提供的下載例子中實現了用後台線程來同步效能計數器和系統時間。Figure 6 解釋了這個實現如何設法讓自己同實際系統時間保持接近(注意縱座標現在被設為+/-100微秒)。 Figure 6 同步例子 Figure 6 顯示了某個 13 分鐘時段高精度時間偏離系統時間的情況。藍線顯示的是在偏離值達到所允許的系統時間偏離值(本例子中是 50 微秒)之前應用周期性再同步的情況。它也表明每次執行後在同步之間的時間增加值。這是因為當前實現的時間供應器適應了效能計數器所報告的頻率計量錯誤,並不斷地將之應用到內部的高精度時間計算上。 雖然藍線顯示的資料應用了平滑過濾,黃線顯示了與系統時間偏差的未經處理資料。這個過濾是即時完成的,並且這是實際用於決定效能計數器真正頻率以及高精度時間與系統時間之間偏離的資料。更多細節,請見下載的原始碼。
防止系統時間受到更改 另外還有系統時間變化的問題。無論何時發生這種事情,你必須立即再同步以便保證計算的時間是正確的。在 Windows 2000 和 Windwos XP 下這到這一點並不困難,因為每當設定系統系統時間時,系統總會廣播一個 WM_TIMECHANGE 訊息到所有的頂層視窗。不幸的是,在 Windows NT 以及更早的版本這不是被強制的,儘管在 SDK 文檔中確實如是說:在改變系統時間後,應用程式應該發送這個訊息到所有的頂層視窗。注意這個句子使用的是“應該”,所以你不能依賴每個人都這麼做。 為了透徹地理解這個問題,我應該說改變系統時間對於任何應用程式來說不什麼特別的事情。為了改變系統時間或相關的配置,需要啟用 SE_SYSTEMTIME_NAME 優先權。如果使用者沒有啟用這個權利,你可以在一個系統管理員帳戶下運行程式,要管理員將這個程式安裝為 Windows NT服務,或者要管理員給運行該程式的帳戶一個必須的許可權。例如,對於 Windows NT 4.0 而言,你最希望的是系統管理員不會或者不允許安裝病態程式(即改變系統時間而不知會其它應用程式)。 所以你如何實際處理 WM_TIMECHNAGE 訊息呢?既然你已經有了一個用於周期性同步的線程,唯一你要做的事情就是讓你的線程建立一個不可見的頂層視窗,並且,除了定期同步外還要運行一個訊息迴圈。
時間調整 與 Windows NT 維護系統時間有關的還有另外一個問題。為了協助軟體例如網路時間協議(Network Time Protocol, NTP)用戶端同外部資源保持時間同步,Windows NT 暴露了一個SetSystemTimeAdjustment API。這個API有兩個參數,以100納秒為單位的時間調節器本身以及一個布爾值,它指示 Windows NT 是否禁用時間調節器。當啟用時間調節器時,系統會在每個時鐘中斷時加上指定的時間調節器的值。當禁用時,系統會用添加預設的時間增量取而代之(在本文中它與時鐘間隔一樣),更多詳情見平台SDK文檔。 但是還有兩個問題。首先啟用(改變)時間調節器改變了參考頻率——時間流。第二,也是一個較大的問題,就是當系統時間被修改後,系統不發送啟用或禁止通知。即使以最小的 156250 個單位(1單位100納秒)預設時間增量改變某個系統上的時間調節器,也將導致參考頻率 6.4PPM (1/156250) 的改變。再一次的,聽起來可能不多,但是考慮一下你要在50微秒內阻止系統時間起變化,那意味著幾秒之後如果沒有進行再同步,你就會超過極限。 為了減少這類調整的衝擊,時間供應器必須監視目前時間調節器的設定。不用藉助於作業系統本身,通過調用SetSystemTimeAdjustment 的夥伴 API GetSystemTimeAdjustment 來實現。在足夠短的間隔內不斷地執行這個檢查並且根據需要調整內部頻率,你就能夠避免偏離系統時間太遠。
時間供應器 現在你已經對問題的各個方面有了較好的理解,我將對下載代碼中的 time_provider 類作一個簡單介紹。這個時間供應器是以參數化單模式方式實現的,為客戶提供了一個高精度,持續更新的時間:template< typename counter_type, int KEEP_WITHIN_MICROS = 100, int SYNCHRONIZE_THREAD_PRIORITY = THREAD_PRIORITY_BELOW_NORMAL, int TUNING_LIMIT_PARTSPERBILLION = 100, int MAX_WAIT_MILLIS = 10000, int MIN_WAIT_MILLIS = 100>class time_provider 使用時間供應器獲得目前時間類似於使用 Windows API:typedef hrt::time_provider<hrt::performance_counter>time_provider_t;time_provider_t& provider=time_provider_t::instance();SYSTEMTIME st;provider.systemtime(&st); Figure 7 解釋了time_provider 類可用的模板參數、類型定義和成員函數。你可能會對將不同的調諧參數被指定為模板參數感到奇怪。從我的觀點來看,他們全都是設計參數,並且可以在編譯時間,根據你的應用程式的需求來確定。 Figure 8 的代碼示範了使用 time_provider 類在一個小迴圈中收集原始時間的例子,然後轉換和輸出。在下載的原始碼中你可以找到另外一個使用多線程的例子(在多線程環境中示範了同樣的想法)。
效能因子 那麼使用 time_provider 類獲得系統時間的開銷有多大呢?當你必須計算時間而不只是擷取時間時,一些額外的工作是不可避免的。如果你確實關心代碼某些臨界部分中的效能,使用 Figure 8 中所示的涉及原始計數器值的技術。使用原始值讓你延遲系統時間轉化,這樣不會立即產生額外的開銷(調用收集計數器本身的值除外,當然,這是不可避免的)。 Figure 9 的表格中顯示得很清楚,它給出了一個 Win32 API 相對於 time_provider 的效能評估。表格中的數字是相對於在Windows XP 對稱式多處理器(SMP)系統上 GetSystemTime 執行時間的百分比(括弧中的數字對應單一處理器系統)。 我在本文前面曾提到,調用 QueryPerformanceCounter 的代價是不能忽略的,對於單一處理器系統尤其如此。使用效能計數器API 呼叫的執行時間在對稱式多處理系統上(SMP)通常要快得多。這是因為大多數對稱式多處理系統的效能計數器中都實現了奔騰時戳計數器(time stamp counter, TSC),與單一處理器系統實現比較調用開銷相對較低。 對於效能我稍微有點失望,即便沒有努力去最佳化計算。為了獲得較好的效能而喪失了可移植性,你可能嘗試使用其它計數器。time_provider 類在計數器類型上是參數化的,可用於其它高精度計數器。下載的原始碼中還有另外一個實驗類 tsc_counter ,可以直接使用奔騰 TSC 。對這個類的初步測試表明:它比使用效能計數器 API 有好得多的效能,甚至是(比效能計數器)在SMP機器上。當進行與 Figure 9 中同樣的測試時,tsc_counter 版本的時間供應器時鐘在 33%(檔案時間),133%(系統時間)和 5.9%(原始時間)。
未來方向 當前的實現還有許多潛在的問題——鑒於問題的複雜性,對此不要感到驚訝。由於硬體相容性所引起的問題,該代碼不可以用在任何可獲得的系統上,比如省電,CPU 超頻以及非持久性計數器。如果在這些條件下你找到辦法使這個供應器更可靠,請讓我知道。在決定使用該代碼之前你應該知道你的硬體平台。 為 .NET 和 COM 進行封裝肯定是可行的,允許時間供應器在除了C++語言之外語言中使用。實際上我已經實現了一個作為 COM 同處理序伺服程式的時間供應器。
結論 如果你現在認為你可以獲得幾乎任意精度的系統時間,給一個小警告:不要忘記像 Windows NT 這樣的搶先式多任務系統,最好的情況下,你獲得的時戳僅僅是讀取效能計數器所花時間並將所讀內容轉化為絕對時間的時間差。最壞的情況下,時間流失會很容易地達到數十毫秒之多。 儘管這有可能預示著你所作的一切都毫無用處,但同時也不見得真的就如此。即使執行對 Win32 API GetSystemTimeAsFileTime (或者 Unix 下的 gettimeofday)的調用也受制於同樣的條件,所以你實際做的不會比那更遭。在大多數情況下,你會得到好的結果。只是不要對基於 Windows NT 的時間戳記有任何實質性的預言。
背景知識
- 時間函數(Time Functions) :Inside Windows 2000,第三版,作者 David Solomon 和 Mark Russionvich(Microsoft Press, 2000)
- 效能計數器值可能會意外地向前跳躍(Performance Counter Value May Unexpectedly Leap Forward)
結束語 效能計數器(Performance Counter)的一些介紹: 在一些電腦硬體系統中,包含有高精度運行計數器,利用它可以獲得高精度定時間隔,其精度與 CPU 的時鐘頻率有關。採用這種方法的步驟如下:
- 1、首先調用 QueryPerformanceFrequency 函數取得高精度運行計數器的頻率f。單位是每秒多少次(n/s),此數一般很大。
- 2、在需要定時的代碼的兩端分別調用 QueryPerformanceCounter 以取得高精度運行計數器的數值n1,n2。兩次數值的差值通過f換算成時間間隔,t=(n2-n1)/f。
|
作者簡介 Johan Nilsson是在 Esrange 的瑞士空間公司的一個系統工程師,位於北極圈之上。自從Windows NT 4.0發布以來他就一直使用C++為Windows NT開發軟體,從Windows 3.1起為Windows/DOS編程。和他聯絡:[email protected] |
|
本文由 VCKBASE MTT 翻譯
Figure 1 獲得和輸出系統時間
#include <windows.h>#include <iostream>#include <iomanip>int main(int argc, char* argv[]){ SYSTEMTIME st; while (true) { ::GetSystemTime(&st); std::cout << std::setw(2) << st.wHour << ‘:‘ << std::setw(2) << st.wMinute << ‘:‘ << std::setw(2) << st.wSecond << ‘.‘ << std::setw(3) << st.wMilliseconds << ‘/n‘; } return 0;}
Figure 2 初始嘗試
#include <windows.h>#include <iostream>#include <iomanip>struct reference_point{FILETIME file_time;LARGE_INTEGER counter;};void simplistic_synchronize(reference_point& ref_point){FILETIME ft0 = {0, 0},ft1 = {0, 0};LARGE_INTEGER li;//// Spin waiting for a change in system time. Get the matching// performace counter value for that time.//::GetSystemTimeAsFileTime(&ft0);do{::GetSystemTimeAsFileTime(&ft1);::QueryPerformanceCounter(&li);}while((ft0.dwHighDateTime == ft1.dwHighDateTime) && (ft0.dwLowDateTime == ft1.dwLowDateTime));ref_point.file_time = ft1;ref_point.counter = li;}void get_time(LARGE_INTEGER frequency, const reference_point&reference, FILETIME& current_time){LARGE_INTEGER li;::QueryPerformanceCounter(&li);//// Calculate performance counter ticks elapsed//LARGE_INTEGER ticks_elapsed;ticks_elapsed.QuadPart = li.QuadPart -reference.counter.QuadPart;//// Translate to 100-nanosecondsintervals (FILETIME// resolution) and add to// reference FILETIME to get current FILETIME.//ULARGE_INTEGER filetime_ticks, filetime_ref_as_ul;filetime_ticks.QuadPart =(ULONGLONG)((((double)ticks_elapsed.QuadPart/(double)frequency.QuadPart)*10000000.0)+0.5);filetime_ref_as_ul.HighPart = reference.file_time.dwHighDateTime;filetime_ref_as_ul.LowPart = reference.file_time.dwLowDateTime;filetime_ref_as_ul.QuadPart += filetime_ticks.QuadPart;//// Copy to result//current_time.dwHighDateTime = filetime_ref_as_ul.HighPart;current_time.dwLowDateTime = filetime_ref_as_ul.LowPart;}int main(int argc, char* argv[]){reference_point ref_point;LARGE_INTEGERfrequency;FILETIMEfile_time;SYSTEMTIMEsystem_time;::QueryPerformanceFrequency(&frequency);simplistic_synchronize(ref_point);while (true){get_time(frequency, ref_point, file_time);::FileTimeToSystemTime(&file_time, &system_time);std::cout << std::setw(2) << system_time.wHour << ‘:‘ << std::setw(2) << system_time.wMinute << ‘:‘ << std::setw(2) << system_time.wSecond << ‘:‘ << std::setw(3) << system_time.wMilliseconds << ‘/n‘;}return 0;}
Figure 7 Time_provider 參數和成員
模板參數 |
counter_type 代表高精度,高頻率的計數器。它必須提供靜態成員值和頻率,同value_type定義一樣。 KEEP_WITHIN_MICROS 定義時間供應器最大可以偏離實際系統時間的微秒個數。它也影響再同步線程的同步頻率。 SYNCHRONIZE_THREAD_PRIORITY 定義同步線程在執行同步時應該設定的自身優先順序。這個不應該被修改除非你的程式不斷的在一個高優先順序上執行。預設的是THREAD_PRIORITY_BELOW_NORMAL,這樣不會打擾正常或高優先順序線程的正常執行。 TUNING_LIMIT_PARTSPERBILLION 目前時間供應器的實現是連續的測量計數器頻率。這個頻率在內部被維護,允許較少頻率的再同步和更準確的定時。當測量的頻率的精確度達到一定閾值時,就不會再執行調整(但周期性再同步總是活動的)。這個極限的單位是計算頻率的錯誤比率,對應的預設值是每10億100單位。 MAX_WAIT_MILLIS 定義允許的最大調諧間隔,毫秒為單位——也就是,檢查高精度時間偏離系統時間有多遠前的等待時間。調諧間隔是自動調整的,但只能達到這個極限。這個參數一般不應該被修改。 MIN_WAIT_MILLIS 定義最小允許的調諧間隔,毫秒為單位。細節見MAX_WAIT_MILLS |
類型定義 |
raw_value_type 能夠儲存“原始”時戳的類型 |
成員函數 |
instance 返回這個類的唯一執行個體的引用 systemtime返回當前的系統時間,格式是SYSTEMTIME結構 filetime 返回當前系統時間,格式是FILETIME結構 rawtime 返回當前系統時間,用最小的負荷返回“原始”時戳。為了把它轉為絕對時間使用filetime_from_rawtime或者systemtime_from_rawtime systemtime_from_rawtime 把“原始”時戳轉為絕對時間,用SYSTEMTIME結構表示 filetime_from_rawtime 把“原始”時戳轉為絕對時間,用FILETIME結構表示 |
Figure 8 使用time_provider類
#include <hrt/performance_counter.hpp>#include <hrt/time_provider.hpp>#include <hrt/system_time.hpp>#include <vector>#include <iostream>#include <iomanip>using namespace hrt;typedef time_provider<performance_counter> time_provider_type;typedef time_provider_type::raw_value_type raw_time_type;typedef std::vector<raw_time_type> raw_vector;const int NUMBER_OF_SAMPLES = 1000;int main(int argc, char* argv[]){ raw_vector samples; time_provider_type& provider = time_provider_type::instance(); samples.reserve(NUMBER_OF_SAMPLES); for (int i = 0; i < NUMBER_OF_SAMPLES; ++i) { samples.push_back(provider.rawtime()); } system_time st; for (raw_vector::iterator iter = samples.begin(); iter != samples.end(); ++iter) { provider.systemtime_from_rawtime(*iter, st.pointer()); std::cout << std::setfill(‘0‘) << std::setw(2) << st.hour() << ‘:‘ << std::setw(2) << st.minute() << ‘:‘ << std::setw(2) << st.second() << ‘.‘ << std::setw(3) << st.millis() << ‘/n‘; } return 0;}
Figure 9 Win32 時間函數和效能
Win32 API |
執行時間 |
time_provider |
執行時間 |
GetSystemTimeAsFileTime |
1.9% (~0%) |
filetime |
135% (900%) |
GetSystemTime |
100% (100%) |
systemtime |
234% (1001%) |
QueryPerformanceCounter |
55% (400%) |
rawtime |
57% (400%) |
同步:有多好?
使用我在文中描述的同步方法,你可以指定你想要的結果精度。然而,實際上,你能得到的結果的品質有平台相關性(硬體和軟體)限制。在 Windows NT 中時鐘中斷處理器需要花費時間來執行,大大地限制了你的精度不可能優於時鐘中斷處理器的執行時間,加上線程環境切換時間,還有當時間變化時調用函數進行檢查所花的時間。如果你在對稱式多處理(SMP)機器上運行,你可以通過在另一個 CPU 上運行同步線程來避免時鐘中斷問題。
在 SMP 機器上禁止同步線程運行在處理時鐘中斷的 CPU 上可以產生數十倍差異的同步精度。唯一的問題是你要首Crowdsourced Security Testing道哪個 CPU 在處理實際的時鐘中斷。從我有限的經驗來看我只能告訴你好像是CPU#0來處理(我想這種感覺有些怪怪的)。假設這是真的,你可以僅僅使用 SetThreadAffinityMask API 從允許處理器的線程列表中移去 CPU#0。你應該通過預先檢查 GetProcessAffinityMask 的調用結果來確認該進程被允許在另一個處理器上運行。
http://blog.csdn.net/jiangxinyu/article/details/2728416
定時器:為 Windows 實現一個連續更新,高精度的時間供應器