关于Linux中延时函数的分析与实践

关于Linux中延时函数的分析与实践

一、简介

  在实际的工程实践中,面对需要程序短暂休眠的情况,我们通常想到的可能是sleep(),usleep(),nanosleep()等函数。但是,在最近阅读代码的过程中,经常会看到使用select()达到延时的目的。本着追根求源(钻牛角尖)的原则,本篇博文,旨在通过具体的实验以及原理分析,从而找出在linux中,关于程序休眠的最佳实践。

二、精度分析

1.测试环境

处理器名称: Intel Core i7
处理器速度: 2.2 GHz
编译器版本:clang-1000.11.45.5

2. 测试例程与结果分析

  本节主要测试在不同延时范围内(秒、毫秒、微秒),上述实现的精度。
测试例程见文件 Sleep.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//
// Sleep.cpp
// Sleep
//
// Created by Litost_Cheng on 2019/3/24.
// Copyright © 2019 Litost_Cheng. All rights reserved.
//

#include <iostream>
#include <sys/time.h>
#include <iostream>
#include <unistd.h>
#include <sys/select.h>
#include <time.h>
using namespace std;
void UserSleep(const timeval &SleepTime);
int main(int argc, const char * argv[])
{

struct timeval SleepTime;

//秒
//5s
memset(&SleepTime, 0, sizeof(timeval));
SleepTime.tv_sec = 5;
UserSleep(SleepTime);
//1s
memset(&SleepTime, 0, sizeof(timeval));
SleepTime.tv_sec = 1;
UserSleep(SleepTime);



//毫秒
//500ms
memset(&SleepTime, 0, sizeof(timeval));
SleepTime.tv_usec = 500000;
UserSleep(SleepTime);


//10ms
memset(&SleepTime, 0, sizeof(timeval));
SleepTime.tv_usec = 10000;
UserSleep(SleepTime);


//微秒
//500us
memset(&SleepTime, 0, sizeof(timeval));
SleepTime.tv_usec = 500;
UserSleep(SleepTime);

//10us
memset(&SleepTime, 0, sizeof(timeval));
SleepTime.tv_usec = 10;
UserSleep(SleepTime);

//1us
memset(&SleepTime, 0, sizeof(timeval));
SleepTime.tv_usec = 1;
UserSleep(SleepTime);



return 0;
}


void UserSleep(const timeval &SleepTime)
{

struct timeval StartTime;
struct timeval EndTime;
long long DelayTime = SleepTime.tv_sec*1000*1000+SleepTime.tv_usec; //延时时间(单位为微妙)
long long ErrorVal = 0; //误差值(单位为微妙)
double ErrorPercentage = 0; //误差百分比
printf("******************Delay [%llu]us ******************\n", DelayTime);
//sleep() 的精度为秒
//为了确保数据可靠性,每组数据采样3组,并取其平均值
if (0 != SleepTime.tv_usec)
{

}
else{

for(int n=0; n<3; n++)
{
memset(&StartTime, 0, sizeof(timeval));
memset(&EndTime, 0, sizeof(timeval));
ErrorVal = 0;
gettimeofday(&StartTime, NULL);
sleep((unsigned int)SleepTime.tv_sec);
gettimeofday(&EndTime, NULL);
ErrorVal += (EndTime.tv_sec*1000*1000 + EndTime.tv_usec) - (StartTime.tv_sec*1000*1000 + StartTime.tv_usec) - DelayTime;

}
ErrorPercentage = (double)ErrorVal/3.0/DelayTime*100;
printf("%-20s error percentage [%10f]\n", "sleep", ErrorPercentage);

}
//usleep() 的精度为毫秒
if (0 != SleepTime.tv_usec%1000)
{

}
else
{
for(int n=0; n<3; n++)
{
memset(&StartTime, 0, sizeof(timeval));
memset(&EndTime, 0, sizeof(timeval));
ErrorVal = 0;
gettimeofday(&StartTime, NULL);
usleep((unsigned int)DelayTime/1000);
gettimeofday(&EndTime, NULL);
ErrorVal += (EndTime.tv_sec*1000*1000 + EndTime.tv_usec) - (StartTime.tv_sec*1000*1000 + StartTime.tv_usec) - DelayTime;

}
ErrorPercentage = (double)ErrorVal/3.0/DelayTime*100;
printf("%-20s error percentage [%10f]\n", "usleep", ErrorPercentage);
}

//select()的精度为微妙
timeval SelectTime = SleepTime;
for(int n=0; n<3; n++)
{
memset(&StartTime, 0, sizeof(timeval));
memset(&EndTime, 0, sizeof(timeval));
ErrorVal = 0;
gettimeofday(&StartTime, NULL);

select(0, NULL, NULL, NULL, &SelectTime);
gettimeofday(&EndTime, NULL);
ErrorVal += (EndTime.tv_sec*1000*1000 + EndTime.tv_usec) - (StartTime.tv_sec*1000*1000 + StartTime.tv_usec) - DelayTime;

}
ErrorPercentage = (double)ErrorVal/3.0/DelayTime*100;
printf("%-20s error percentage [%10f]\n", "select", ErrorPercentage);


//nanosleep()的精度为纳秒,但是我们不对纳秒进行测试
for(int n=0; n<3; n++)
{
memset(&StartTime, 0, sizeof(timeval));
memset(&EndTime, 0, sizeof(timeval));
ErrorVal = 0;

timespec TempTime;
TempTime.tv_sec = SleepTime.tv_sec;
TempTime.tv_nsec = SleepTime.tv_usec/1000;
gettimeofday(&StartTime, NULL);
nanosleep(&TempTime, NULL);
gettimeofday(&EndTime, NULL);
ErrorVal += (EndTime.tv_sec*1000*1000 + EndTime.tv_usec) - (StartTime.tv_sec*1000*1000 + StartTime.tv_usec) - DelayTime;

}
ErrorPercentage = (double)ErrorVal/3.0/DelayTime*100;
printf("%-20s error percentage [%10f]\n", "nanosleep", ErrorPercentage);
}

具体的测试结果如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
******************Delay [5000000]us ******************
sleep error percentage [ 0.027747]
usleep error percentage [-33.296087]
select error percentage [ 0.002680]
nanosleep error percentage [ 0.033160]
******************Delay [1000000]us ******************
sleep error percentage [ 0.086333]
usleep error percentage [-33.291200]
select error percentage [ 0.167867]
nanosleep error percentage [ 0.155300]
******************Delay [500000]us ******************
usleep error percentage [-33.290600]
select error percentage [ 0.313333]
nanosleep error percentage [-33.332200]
******************Delay [10000]us ******************
usleep error percentage [-33.230000]
select error percentage [ 3.433333]
nanosleep error percentage [-33.253333]
******************Delay [500]us ******************
select error percentage [ 10.600000]
nanosleep error percentage [-33.200000]
******************Delay [10]us ******************
select error percentage [ 36.666667]
nanosleep error percentage [-26.666667]
******************Delay [1]us ******************
select error percentage [400.000000]
nanosleep error percentage [ 33.333333]
Program ended with exit code: 0

  在该测试例程中,我们分别选取了5s、1s、500ms、10ms、500us、10us、1us等延时时间,并对sleep(),usleep(),select(),nanosleep()等函数进行了测试(由于每个函数的本身的延时范围存在差别,因此,仅在满足其相应的条件的前提的对其进行测试)。

  具体的测试方式为:

  • 通过gettimeofday(),获取休眠前的起始时间A
  • 调用相应API,进行休眠,休眠时间为B
  • 休眠结束后,再次调用gettimeofday()获取休眠结束实践C
  • (C-A)*100/B获取相应的误差百分比

注:为了保证测试的准确性,对每种情况均进行3次测试,并求其平均值

  从测试结果中,我们不难发现:

  • 在延时时间为秒级时

  sleep(),select(),nanosleep()都有着较高的精度

  • 延时时间为毫秒级时

  select()有着明显优于usleep()以及nanosleep()的表现

  • 延时时间为微秒级时

  在时间粒度相对较大的情况下,select()的表现优于以及nanosleep(),但随着延时时间的进一步缩小,,select()的精确度,越来越差。而nanosleep()则表现的较为稳定。

  综上所述:用 select() 来实现微秒级以上的延时,是一种最佳选择

三、实现原理




  1. sleep usleep select 延时比较:https://blog.csdn.net/zhoujunyi/article/details/1546330

五、文档信息


作者: Litost_Cheng

发表日期:2019年03月24日
更多内容:

  1. Litost_Cheng的个人博客
  2. Litost_Cheng的Github
  3. Litost_Cheng的博客
Title - Artist
0:00