关于Linux中延时函数的分析与实践
一、简介
在实际的工程实践中,面对需要程序短暂休眠的情况,我们通常想到的可能是sleep(),usleep(),nanosleep()等函数。但是,在最近阅读代码的过程中,经常会看到使用select()达到延时的目的。本着追根求源(钻牛角尖)的原则,本篇博文,旨在通过具体的实验以及原理分析,从而找出在linux
中,关于程序休眠的最佳实践。
二、精度分析
1.测试环境
处理器名称: Intel Core i7
处理器速度: 2.2 GHz
编译器版本:clang-1000.11.45.5
2. 测试例程与结果分析
本节主要测试在不同延时范围内(秒、毫秒、微秒),上述实现的精度。
测试例程见文件 Sleep.cpp
:
//
// 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);
}
具体的测试结果如下图所示:
******************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()
来实现微秒级以上的延时,是一种最佳选择
三、实现原理
...四、参考与链接
- sleep usleep select 延时比较:https://blog.csdn.net/zhoujunyi/article/details/1546330
五、文档信息
作者: Litost_Cheng发表日期:2019年03月24日
更多内容: