Windows系統 ms級精確延時實現


一、前言

  因為接手的一個項目要做到精確到1ms以內的定時觸發功能,在測試過 Sleep(DWORD millsecond)函數的准確度之后,該函數不能滿足要求。上網查詢了相詢了相關資料,現將測試過程總結一下,方便自己以后翻閱。

二、測試過程

  1.開發平台:系統windows 7 + vs2013 + win32

  2.相關代碼

  代碼1如下所示:

  1 #include "stdafx.h"
  2 #include <iostream>
  3 #include <vector>
  4 #include "math.h"
  5 #include "windows.h"
  6 #include "stdio.h"
  7 
  8 using namespace std;
  9 
 10 //是否調用Sleep()函數的閾值
 11 #define VALUE_NO_SLEEP 8
 12 
 13 
 14 
 15 ///
 16 //函數功能:精確延時函數
 17 //函數參數:延時時間
 18 //返回值:實際延時時間
 19 double MyDelay(double millsecond)
 20 {
 21     LARGE_INTEGER litmp;
 22     LONGLONG qt1, qt2;
 23     double dft, dff, dfm;
 24 
 25     //獲得時鍾頻率
 26     QueryPerformanceFrequency(&litmp);//獲得時鍾頻率
 27     dff = (double)litmp.QuadPart;
 28 
 29     //獲得初始值
 30     QueryPerformanceCounter(&litmp);
 31     qt1 = litmp.QuadPart;
 32 
 33     double delayTime = 0;
 34     if (millsecond > VALUE_NO_SLEEP)        //10ms 的效果會比較好,效果好於5ms、6ms、8ms
 35     {
 36         Sleep(millsecond - VALUE_NO_SLEEP);
 37     }
 38 
 39     //程序延時
 40     do
 41     {
 42         //除法運算用於耗時
 43         double tmp = 1.0 / 3;
 44         QueryPerformanceCounter(&litmp);
 45         qt2 = litmp.QuadPart;
 46 
 47         //獲得精確時間值,轉到毫秒單位上
 48         dft = (double)(qt2 - qt1)/dff;
 49         //dft = dfm / dff;
 50         //printf("當前用時: %.3f 毫秒\n", dft*1000.0);
 51     } while (dft * 1000 < millsecond);
 52     //結束時用時
 53     //printf("實際用時: %.3f 毫秒\n", dft*1000.0);
 54     return dft*1000.0;
 55 
 56 }
 57 
 58 
 59 
 60 int _tmain(int argc, _TCHAR* argv[])
 61 {
 62 
 63 
 64     double time = MyDelay(45);
 65     printf("實際延時時間:%.3f\n", time);
 66 
 67 
 68     //測試一:
 69     //記錄時間
 70     vector<double> record;
 71 
 72     LARGE_INTEGER ts, te, tc;
 73     double ds, de;
 74     LONGLONG qts, qte;
 75     //獲取時鍾頻率
 76     QueryPerformanceFrequency(&tc);
 77     ds = (double)tc.QuadPart;
 78 
 79     for(int i=0;i<50;++i)
 80     {
 81         //獲取初始值
 82         QueryPerformanceCounter(&ts);
 83         qts = ts.QuadPart;
 84 
 85         //做運算,用於耗時
 86         double arr[20480] = {0.0};
 87         for(int i=0;i<20480;++i)
 88         {
 89             arr[i] = double(i+1)/3;
 90         }
 91         //精確獲取計算時間
 92         QueryPerformanceCounter(&ts);
 93         qte = ts.QuadPart;
 94 
 95         //獲得精確時間值,轉到毫秒單位上
 96         de = (double)(qte - qts) / ds;
 97         //實際耗時
 98         de *= 1000.0;    
 99         printf("除法運算耗時:%.3f毫秒\n", de);
100         record.push_back(de);
101         double tmp = MyDelay(50.0 - de);
102         printf("精確延時:%.3f毫秒\n",tmp);
103         record.push_back(tmp);
104         //實際共耗時
105         QueryPerformanceCounter(&ts);
106         qte = ts.QuadPart;
107         //獲得精確時間值,轉到毫秒單位上
108         de = (double)(qte - qts) / ds;
109         //實際耗時
110         de *= 1000.0;
111         printf("實際共耗時:%.3f毫秒\n\n",de);
112         record.push_back(de);
113     }
114 
115     FILE* fp = fopen("record.txt", "a+");
116     if(fp != NULL)
117     {
118         for(int i=0;i<record.size();++i)
119         {
120             char info[16] = {0};
121             sprintf(info, "%.4f\n", record[i]);
122             fwrite(info,1,strlen(info), fp);
123         }
124         fclose(fp);
125     }
126     else
127     {
128         fclose(fp);
129         return -1;
130     }
131 
132     system("pause");
133     return 0;
134 }
View Code

  代碼1的命令行輸出不滿足設計想法,輸出如下所示:

//單獨測試MyDelay(45)的輸出結果
實際延時時間:45.836

除法運算耗時:0.526毫秒
精確延時:49.474毫秒
實際共耗時:51.900毫秒

除法運算耗時:0.499毫秒
精確延時:49.501毫秒
實際共耗時:51.882毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:51.820毫秒

除法運算耗時:0.500毫秒
精確延時:49.500毫秒
實際共耗時:51.792毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:51.805毫秒

除法運算耗時:0.503毫秒
精確延時:49.497毫秒
實際共耗時:51.696毫秒

除法運算耗時:0.503毫秒
精確延時:49.497毫秒
實際共耗時:52.944毫秒

除法運算耗時:0.503毫秒
精確延時:49.497毫秒
實際共耗時:54.605毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.843毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:52.848毫秒

除法運算耗時:0.500毫秒
精確延時:49.500毫秒
實際共耗時:52.955毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.718毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.433毫秒

除法運算耗時:0.303毫秒
精確延時:49.698毫秒
實際共耗時:51.968毫秒

除法運算耗時:0.298毫秒
精確延時:49.702毫秒
實際共耗時:52.010毫秒

除法運算耗時:0.309毫秒
精確延時:49.691毫秒
實際共耗時:53.467毫秒

除法運算耗時:0.300毫秒
精確延時:49.700毫秒
實際共耗時:51.943毫秒

除法運算耗時:0.295毫秒
精確延時:49.705毫秒
實際共耗時:52.539毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:52.724毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.609毫秒

除法運算耗時:0.500毫秒
精確延時:49.500毫秒
實際共耗時:52.753毫秒

除法運算耗時:0.503毫秒
精確延時:49.497毫秒
實際共耗時:52.756毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:52.622毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:52.799毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.687毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.875毫秒

除法運算耗時:0.503毫秒
精確延時:49.497毫秒
實際共耗時:52.738毫秒

除法運算耗時:0.503毫秒
精確延時:49.497毫秒
實際共耗時:52.864毫秒

除法運算耗時:0.523毫秒
精確延時:49.477毫秒
實際共耗時:54.418毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.700毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:53.040毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.781毫秒

除法運算耗時:0.503毫秒
精確延時:49.497毫秒
實際共耗時:53.402毫秒

除法運算耗時:0.503毫秒
精確延時:49.497毫秒
實際共耗時:54.049毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.694毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:52.861毫秒

除法運算耗時:0.503毫秒
精確延時:49.497毫秒
實際共耗時:52.686毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.691毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.738毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:52.646毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:53.498毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.701毫秒

除法運算耗時:0.506毫秒
精確延時:49.495毫秒
實際共耗時:54.056毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:52.662毫秒

除法運算耗時:0.501毫秒
精確延時:49.499毫秒
實際共耗時:52.678毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:52.821毫秒

除法運算耗時:0.503毫秒
精確延時:49.497毫秒
實際共耗時:52.664毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:52.749毫秒

除法運算耗時:0.502毫秒
精確延時:49.498毫秒
實際共耗時:52.671毫秒

除法運算耗時:0.500毫秒
精確延時:49.500毫秒
實際共耗時:52.922毫秒
View Code

  

  再次查看代碼時,產生疑惑:是不是多余的乘法運算和printf()輸出導致【實際共耗時】與【除法運算耗時】、【精確延時】的加和相差較大的現象。立即着手屏蔽這些代碼,並且只記錄每次for循環開始時刻的精確時間,代碼2如下所示:

  1 #include "stdafx.h"
  2 #include <iostream>
  3 #include <vector>
  4 #include "math.h"
  5 #include "windows.h"
  6 #include "stdio.h"
  7 
  8 using namespace std;
  9 
 10 
 11 #define VALUE_NO_SLEEP 8
 12 
 13 
 14 
 15 ///
 16 //函數功能:精確延時函數
 17 //函數參數:延時時間
 18 //返回值:實際延時時間
 19 double MyDelay(double millsecond)
 20 {
 21     LARGE_INTEGER litmp;
 22     LONGLONG qt1, qt2;
 23     double dft, dff, dfm;
 24 
 25     //獲得時鍾頻率
 26     QueryPerformanceFrequency(&litmp);//獲得時鍾頻率
 27     dff = (double)litmp.QuadPart;
 28 
 29     //獲得初始值
 30     QueryPerformanceCounter(&litmp);
 31     qt1 = litmp.QuadPart;
 32 
 33     double delayTime = 0;
 34     if (millsecond > VALUE_NO_SLEEP)
 35     {
 36         Sleep(millsecond - VALUE_NO_SLEEP);
 37     }
 38 
 39     //程序延時
 40     do
 41     {
 42         //除法運算用於耗時
 43         double tmp = 1.0 / 3;
 44         QueryPerformanceCounter(&litmp);
 45         qt2 = litmp.QuadPart;
 46 
 47         //獲得精確時間值,轉到毫秒單位上
 48         dft = (double)(qt2 - qt1) / dff;
 49         //dft = dfm / dff;
 50         //printf("當前用時: %.3f 毫秒\n", dft*1000.0);
 51     } while (dft * 1000 < millsecond);
 52     //結束時用時
 53     //printf("實際用時: %.3f 毫秒\n", dft*1000.0);
 54     return dft*1000.0;
 55 
 56 }
 57 
 58 
 59 int _tmain(int argc, _TCHAR* argv[])
 60 {
 61 
 62     //測試MyDelay函數
 63     double time = MyDelay(45);
 64     printf("實際延時時間:%.3f\n", time);
 65 
 66 
 67     //測試二:不做冗余的計算和打印,只記錄循環開始的精確時間
 68     //記錄時間
 69     vector<LONGLONG> record2;
 70     LARGE_INTEGER ts, te, tc;
 71     double ds, de;
 72     LONGLONG qts, qte;
 73     //獲取時鍾頻率
 74     QueryPerformanceFrequency(&tc);
 75     ds = (double)tc.QuadPart;
 76     //循環1000次,以便觀察,消除偶然性
 77     for (int i = 0; i < 1000; ++i)
 78     {
 79         //獲取初始值
 80         QueryPerformanceCounter(&ts);
 81         qts = ts.QuadPart;
 82         record2.push_back(qts);
 83         //做運算
 84         double arr[20480] = { 0.0 };
 85         for (int i = 0; i < 20480; ++i)
 86         {
 87             arr[i] = double(i + 1) / 3;
 88         }
 89 
 90         //除法運算實際耗時
 91         QueryPerformanceCounter(&ts);
 92         qte = ts.QuadPart;
 93         //獲得精確時間值,轉到毫秒單位上
 94         de = (double)(qte - qts) / ds * 1000.0;
 95         //printf("除法運算耗時:%.3f毫秒\n", de);
 96         double tmp = MyDelay(50.0 - de);
 97         //去除多余的計算和查詢時間,每次只記錄循環開始的精確時間
 98     }
 99 
100     FILE* fp = fopen("record.txt", "w+");
101     char *data = new char[10240];
102     memset(data, 0, sizeof(data));
103     if (fp != NULL)
104     {
105         for (int i = 0; i < record2.size() - 1; ++i)
106         {
107             //輸出前后兩次記錄值之間的差值,即一次循環的耗時
108             de = (double)(record2[i + 1] - record2[i]) / ds * 1000.0;
109             char info[16] = { 0 };
110             sprintf(info, "%.4f\n", de);
111             strcat(data, info);
112         }
113         fwrite(data, 1, strlen(data), fp);
114         fclose(fp);
115         delete[]data;
116         data = NULL;
117     }
118     else
119     {
120         fclose(fp);
121         return -1;
122     }
123 
124     system("pause");
125     return 0;
126 }
View Code


  代碼2的輸出這里就不貼出了。有興趣的可以自己測試一下,如果有問題,可以聯系我,大家一起討論和學習。

三、參考資料

1、精確獲取時間(QueryPerformanceCounter)  
2、如何做到精確延時  


免責聲明!

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



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