《X86-Linux下高精度延时方案的实现(10us误差)》

目录

问题描述

解决思路

柳暗花明

解决办法


Linux实现高精度延时,网上大部分方法只能实现50us左右的延时精度,今天我们来看下董总是如何解决的,将延时精度提升到10us。

问题描述

朋友最近项目上在开发Ethercat主站,需要用到高精度的延时机制,设计需求是1000us周期下,误差不能超过1%(10us)

由于项目硬件方案是intel的处理器X86,熟悉linux的人都知道这个很难实现,当时评估方案的时候有些草率,直接用的PREEMPT_RT补丁+内核hrtimer+signal通知的方式来评估的。当时验证的结果也很满意,于是兴冲冲的告诉领导说方案可行,殊不知自己挖了一个巨大的坑。。。

实际项目开始的时候,发现这个方案根本行不通,有两个原因:

  • signal通知只能通知到进程,而目前移植的方案无法做到被通知的进程中无其他线程。这样高频的signal发过来,其他线程基本上都会被干掉。(补充说明:这里特指的是内核驱动通知到应用层,在用户层中是有专门的函数可以通知不同线程的。并且这个问题经过研究,可以通过设置线程的sigmask来解决,但是依旧无法改变方案行不通的结论)

  • 这也是主要原因,Ethercat的同步周期虽然可以在程序开始时固定,但是实际运行时运行周期是需要动态调整的,调整范围在5us以内。这样一来,动态调整hrtimer的开销就变得无法忽略了,换句话说,我们需要的是一个延时机制,而不是定时器。

所以这个方案被PASS了。

解决思路

既然signal不行,那只能通过其他手段来分析。总结下来我大致进行了如下的尝试:

一、sleep方案的确定:尝试过usleep,nanosleep,clock_nanosleep,cond_timedwait,select等,最终确定用clock_nanosleep,选它的原因并不是因为它支持ns级别的精度。因为经过测试发现,上述几个调用在周期小于10000us的情况下,精度都差不多,误差主要都来自于上下文切换的开销。选它的主要原因是因为它支持选项叫TIME_ABSTIME,这个选项的意思是支持绝对时间。这里举个简单的例子,解释一下为什么要用绝对时间:

while(1)
{do_work();sleep(1);do_post();
}

假设上面这个循环,我们目的是让do_post的执行以1s的周期执行一次,但是实际上,不可能是绝对的1s,因为sleep()只能延时相对时间,而目前这个循环的实际周期是do_work的开销+sleep(1)的时间。所以这种开销放在我们需求的场景中,就变得无法忽视了。而用clock_nanosleep的好处就是一方面它可以选择时钟源,其次就是它支持绝对时间唤醒,这样我在每次do_work之前都设置一下clock_nanosleep下一次唤醒时的绝对时间,那么clock_nanosleep实际执行的时间其实就会减去do_work的开销,相当于是闹钟的概念。

二、改用实时线程:将重要任务的线程改成实时线程,调度策略改成FIFO,优先级设到最高,减少被抢占的可能性。

三、设置线程的亲和性:对应用下所有线程进行规划,根据负载情况将几个负载比较重的任务线程分别绑定到不同的CPU核上,这样减少切换CPU带来的开销。

四、减少不必要的sleep调用:由于很多任务都存在sleep调用,我用strace命令分析了整个应用sleep系统调用的比例,高达98%,这种高频次休眠+唤醒带来的开销势必是不可忽略的。所以我将main循环中的sleep改成了循环等待信号量的方式,因为pthread库中信号量的等待使用了futex,它使得唤醒线程的开销会小很多。其他地方的sleep也尽可能的优化掉。这个效果其实比较明显,能差不多减少20us的误差

五、绝招:从现有应用中剥离出最小任务,减少所有外界任务的影响

经过上述五点,1000us的误差从一开始的±100us,控制到了±40us。但是这还远远不够。。。
黔驴技穷的我开始漫长的Google+Baidu ing。。。。
这期间也发现了一些奇怪的现象,比如下面这张图。

图片是用python对抓包工具的数据进行分析生成的,参考性不用质疑。纵轴代表实际这个周期所耗费的时间。可以发现很有意思的现象:

1. 每隔一定周期,会集中出现规模的误差抖动

2. 误差不是正态分布,而是频繁出现在±30us左右的地方

3. 每次产生较大的误差时,下个周期一定会出现一次反向的误差,而且幅度大致相同(这点从图上看不出来,通过其他手段分析的)。

简单描述一下就是假设这个周期的执行时间是980us,那下个周期的执行时间一定会在1020us左右。

第1点和第2点可以经过上面的4条优化措施消除,第3点没有找到非常有效的手段,我的理解可能内核对这种误差是知晓的并且有意在弥补,如果有知道相关背后原理的大神欢迎分享一下。

针对这个第三点奇怪的现象我也尝试做了手动的干预,比如设一个阈值,当实际程序执行的误差大于这个阈值时,我就在设置下一个周期的唤醒时间时,手动减去这个误差,但是运行效果却大跌眼镜,更差了。。。

柳暗花明

在尝试了200多次参数调整,被这个问题卡了一个多礼拜之后,也不知道当时打了什么搜索的关键字,偶然发现了一篇dell的文档。终于解决了这个难题,文档标题是:

随后经过一番针对性的查找终于摸清了来龙去脉:
原来Intel的cpu为了节能,有很多功耗模式,简称C-states。

当程序运行的时候,CPU是在C0状态,但是一旦操作系统进入休眠,CPU就会用Halt指令切换到C1或者C1E模式,这个模式下os如果进行唤醒,那么上下文切换的开销就会变大!

这个选项按道理BIOS是可以关掉的,但是坑的地方就在于版本相对较新的linux内核版本,默认是开启这个状态的,并且是无视BIOS设置的!这就很坑了!

针对性查找之后,发现网上也有网友测试,2.6版本的内核不会默认开启这个,但是3.2版本的内核就会开启,而且对比测试发现,这两个版本内核在相同硬件的情况下,上下文切换开销可以相差10倍,前者是4us,后者是40-60us。

解决办法

一、久修改可以修改linux的引导参数,修改/etc/default/grub文件中的GRUB_CMDLINE_LINUX_DEFAULT选项,改成下面的内容:

intel_idle.max_cstate=0 processor.max_cstate=0 idle=poll

然后使用update-grub命令使参数生效,重启即可。

二、动态修改可以通过往/dev/cpu_dma_latency这个文件中写值,来调整C1/C1E模式下上下文切换的开销。我选择是写0,就直接关闭。当然你也可以选择写一个数值,这个数值就代表上下文切换的开销,单位是us。比如你写1,那么就是设置开销为1us。当然这个值是有范围的,这个范围在/sys/devices/system/cpu/cpuX/cpuidle/stateY/latency文件中可以查到,X代表具体哪个核,Y代表对应的idle_state。

至此,这个性能问题就得到了完美的解决,目前稳定测试的性能如下图所示:

实现了X86-Linux下高精度延时1000us精确延时,精度10us。

x86 高精度延时方案实现(10us误差) | 功耗模式C-states相关推荐

  1. STM32 进阶教程 4 - 软件实现高精度延时 2

    前言 软件工程师在在实际项目开发过程中,软件延时想必或多或少都有接触过,诸如delay_s(x); delay_ms(x);delay_us(x)等类型的延时函数大家也一定见过或自已曾经实现过,本节给 ...

  2. NXP (I.MX6ULL) GPT高精度延时定时器

    参考:Linux NXP (I.MX6ULL) GPT高精度延时定时器 作者:一只青木呀 发布时间: 2020-09-20 11:50:14 网址:https://blog.csdn.net/weix ...

  3. 新增量赛道启动,车规级高精度定位方案商竞争力TOP10

    今年2月,六分科技宣布完成6.2亿元B轮融资,加快布局车规级高精度定位完整解决方案.这是继2019年千寻位置完成10亿元A轮融资后,汽车高精度定位赛道第二大单笔融资金额. 两家公司都是定位于提供高精度 ...

  4. 休眠 嵌入式_内幕消息:嵌入式软件挤出最低功耗模式

    低功耗运行仍然是各行业应用的关键驱动因素.随着睡眠模式的增加,电源管理突然从单纯的硬件问题转移到软件开发人员必须考虑的事情上. 功耗模式的最简单应用是当系统空闲时,将其置于休眠状态.然而,今天的MCU ...

  5. Jetson TX2 power mode功耗模式

    一.参考资料 TX2 五种功耗模式 NVIDIA Jetson AGX Xavier设置风扇转速.工作模式及性能监控 二.相关介绍 Jetson TX2由一个GPU和一个CPU集群组成,CPU集群由双 ...

  6. Redis高可用方案:sentinel(哨兵模式)和集群

    一. redis高可用方案–sentinel(哨兵模式) 当我们搭建好redis主从复制方案后会发现一个问题,那就是当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力, ...

  7. TX2的开机测试 刷机过程 功耗模式选择 源更新 语言修改 拼音输入法设置

    以下博文主要为记录刷机过程,为后续刷机提供帮助,其中的图片并非我截图而是借鉴别的博客,重点在于说明问题. 博客主要参考一下: https://blog.csdn.net/qq_38880380/art ...

  8. 低延时直播与RTC融合架构设计②:直播与RTC低延时方案

    本文整理自网易云信多媒体资深技术架构师吴桐在 QCon 全球软件开发大会上海站的演讲内容<超高清4K视频低延时直播与RTC融合架构设计>,为该系列的第二篇文章. 回顾该系列第一篇文章< ...

  9. mediacodec延时_FFmpeg优化点播延时方案

    场景要求 项目要求点播速度是300到500毫秒之间,现在最长的点播延时是1300毫秒(有的时候甚至无法播放视频),生产环境是RTSP传输h264裸流数据,研究在接收到I帧的时候,开始出来图像,简化FF ...

最新文章

  1. 什么是采样层(pooling)
  2. 石墨烯新新新应用,MIT大规模生产细胞大小机器人,有感知能存储
  3. 超强、超详细Redis数据库入门教程
  4. linux 修改编码
  5. 哪些医药企业使用SAP系统呢?
  6. 不重复的两两比较(洛谷P5728题题解,Java语言描述)
  7. 父元素遮住子元素的问题
  8. 事件库之Redis自己的事件模型-ae
  9. ORACLE Physical Standby 级联备库搭建
  10. POJ1088(dp)
  11. Centos / Ubuntu /linux/mac JAVA 环境变量设置
  12. windows黑客编程系列(一):运行单一实例
  13. java将阿拉伯数字转换为中文数字
  14. 一个普通前端女孩的一年|2021总结
  15. iMeta: 整合宏组学重新认识生命和环境科学
  16. 2011考研数学二第(11)题——第一类曲线积分球弧长
  17. linux创建磁盘的命令,linux中创建磁盘分区的命令是什么
  18. C语言结构体typedef struct详解
  19. NORDIC Thingy:52 蓝牙 BLE 服务 SoC 程序调用流程分析之八, 网盘分享 PPT
  20. dSYM文件解析与分析

热门文章

  1. 2021高考成绩怎么查询时间北京,2021年北京高考成绩几号公布可以查询,查询时间安排...
  2. solr通过连接数据库删除document 总结
  3. Vue-Treeselect 的下拉菜单不出Table的框
  4. leetcode题解53-最大子序和
  5. js中数组的操作方法
  6. vue-i18n和ElementUI国际化使用
  7. 有了Auto Layout,为什么你还是害怕写UITabelView的自适应布局?
  8. 插件 KSImageNamed 用图片时自动显示图片缩略图
  9. shell脚本之从1加到100之和的思路
  10. cursor -- 定义鼠标样式