一、前言
因为接手的一个项目要做到精确到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、如何做到精确延时