如何計算毫秒級的時間差


計算毫秒級的時間差算是一個常見的需求吧...

 

手頭上是windows編程的項目,所以首先就想到的是GetTickCount(),但MSDN上這么說:

寫個程序試一下吧:

 1 #include <stdio.h>
 2 #include <windows.h>
 3 
 4 int main(void) 
 5 {
 6     DWORD dwLastTime = GetTickCount();
 7     for (int i = 0; i != 10; ++i)
 8     {
 9         DWORD dwCurrentTime = GetTickCount();
10         printf("GetTickCount = %ldms TimeDiff = %ldms\n", dwCurrentTime, dwCurrentTime - dwLastTime);
11         dwLastTime = dwCurrentTime;
12         Sleep(500);
13     }
14     return 0;
15 }
View Code

 

可以看到,算了10次,每次偏差一般都有1ms,更有甚者,達到15ms,跟MSDN里說的實際精度一樣。

所以,用GetTickCount()計算毫秒級的時間差是不靠譜的

 

那下面,如何滿足我們的需求呢?

需求1:計算毫秒級別的時間差。

需求2:返回值最好是unsigned long級別的,以便與現有代碼保持兼容。

 

解決方案1:

clock_t clock(void);

這個函數返回的是從程序啟動到當前時刻所經歷的CPU時鍾周期數。將這個函數封裝一下即可:

1 #include <ctime>
2 
3 ULONG GetTickCountClock()
4 {
5     return (ULONG)((LONGLONG)clock() * 1000 / CLOCKS_PER_SEC);
6 }

測試結果:

 

解決方案2:

SYSTEMTIME FILETIME 

通過SYSTEMTIME和FILETIME,我們可以得到距離1601年1月1日凌晨所經歷的時間,單位是100納秒。

這個時間肯定是足夠精確了,但是得到的數值是一個LONGLONG,沒關系,我們可以用這個時間來校准原生的GetTickCount()。

 1 ULONG GetTickCountCalibrate()
 2 {
 3     static ULONG s_ulFirstCallTick = 0;
 4     static LONGLONG s_ullFirstCallTickMS = 0;
 5 
 6     SYSTEMTIME systemtime;
 7     FILETIME filetime;
 8     GetLocalTime(&systemtime);    
 9     SystemTimeToFileTime(&systemtime, &filetime);
10     LARGE_INTEGER liCurrentTime;
11     liCurrentTime.HighPart = filetime.dwHighDateTime;
12     liCurrentTime.LowPart = filetime.dwLowDateTime;
13     LONGLONG llCurrentTimeMS = liCurrentTime.QuadPart / 10000;
14 
15     if (s_ulFirstCallTick == 0)
16     {
17         s_ulFirstCallTick = GetTickCount();
18     }
19     if (s_ullFirstCallTickMS == 0)
20     {
21         s_ullFirstCallTickMS = llCurrentTimeMS;
22     }
23 
24     return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);
25 }

測試結果:

 

精度比較

每隔50ms獲取一次當前時刻,對比TimeDiff與50之間的差距,統計1000次:

 1 #include <math.h>
 2 
 3 int main(void) 
 4 {
 5     int nMaxDeviation = 0;
 6     int nMinDeviation = 99;
 7     int nSumDeviation = 0;
 8     
 9     DWORD dwLastTime = GetTickCountCalibrate();
10     Sleep(50);
11     
12     for (int i = 0; i != 1000; ++i)
13     {
14         DWORD dwCurrentTime = GetTickCountCalibrate();
15         int nDeviation= abs(dwCurrentTime - dwLastTime - 50);
16         nMaxDeviation = nDeviation > nMaxDeviation ? nDeviation : nMaxDeviation;
17         nMinDeviation = nDeviation < nMinDeviation ? nDeviation : nMinDeviation;
18         nSumDeviation += nDeviation;
19         dwLastTime = dwCurrentTime;
20         Sleep(50);
21     }
22     printf("nMaxDeviation = %2dms, nMinDeviation = %dms, nSumDeviation = %4dms, AverDeviation = %.3fms\n", 
23            nMaxDeviation, nMinDeviation, nSumDeviation, nSumDeviation / 1000.0f);
24     
25     return 0;
26 }
View Code

比較GetTickCount、GetTickCountClock、GetTickCountCalibrate的精度如下:

GetTickCount           nMaxDeviation = 13ms, nMinDeviation = 3ms, nSumDeviation = 5079ms, AverDeviation = 5.079ms
GetTickCountClock      nMaxDeviation =  2ms, nMinDeviation = 0ms, nSumDeviation =    4ms, AverDeviation = 0.004ms
GetTickCountCalibrate  nMaxDeviation =  1ms, nMinDeviation = 0ms, nSumDeviation =    3ms, AverDeviation = 0.003ms

可以看到,原生的GetTickCount誤差過大,最大誤差13ms,平均誤差5ms,肯定無法滿足毫秒級的計時需求。

GetTickCountClock與GetTickCountCalibrate在精度上相差無幾,都可以滿足毫秒級的計時需求。

區別在於,GetTickCountClock是從當前程序運行開始計時,GetTickCountCalibrate是從系統啟動開始計時

 

有關溢出

4個字節的ULONG最大值是4294967296ms,也就是49.7天,超過這個值就會溢出。

 

 [轉載請保留本文地址:http://www.cnblogs.com/goagent/p/4083812.html]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM