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

关于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日
更多内容:

  1. Litost_Cheng的博客
  2. Litost_Cheng的Github
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值