在具有硬实时约束的嵌入式应用程序(用 C 语言编写,在 32 位处理器上)中,关键代码(特别是中断)的执行时间需要保持恒定。
您如何确保在代码执行中不会引入时间可变性,特别是由于处理器的缓存(无论是 L1、L2 还是 L3)?
请注意,我们关注缓存行为,因为它对执行速度有巨大影响(有时超过 100:1 与访问 RAM)。由于特定处理器架构而引入的可变性远不及高速缓存的量级。
两种可能:
完全禁用缓存。应用程序将运行较慢,但没有任何变化。
将代码预加载到缓存中并“锁定”。大多数处理器都提供了一种机制来做到这一点。
似乎您指的是 x86 处理器系列,它没有考虑到实时系统,因此无法真正保证恒定时间执行(CPU 可能会重新排序微指令,而不是分支预测和指令预取队列)每次当 CPU 错误地预测条件跳转时都会刷新...)
如果您可以使用硬件,或者与可以使用的人合作,您可以关闭缓存。一些 CPU 有一个引脚,如果将其连接到地而不是电源(或者可能是其他方式),将禁用所有内部缓存。这将提供可预测性,但不会提供速度!
如果做不到这一点,也许在软件代码的某些地方可以编写故意用垃圾填充缓存,因此接下来发生的任何事情都可以保证是缓存未命中。做得对,可以提供可预测性,并且可能只能在某些地方完成,因此速度可能比完全禁用缓存更好。
最后,如果速度确实很重要——仔细设计软件和数据,就像在过去为古老的 8 位 CPU 编程一样——保持它足够小,以使其全部适合 L1 缓存。我总是惊讶于这些天的板载缓存如何比小型计算机上的所有 RAM 都大(mumble-decade)。但这将是一项艰苦的工作,并且需要聪明才智。祝你好运!
这个答案听起来很讽刺,但它旨在让您思考:
只运行一次代码。
我这么说的原因是因为太多会使它变得可变,您甚至可能无法控制它。你对时间的定义是什么?假设操作系统决定将您的进程放入等待队列。
其次,由于缓存性能、内存延迟、磁盘 I/O 等原因,您会遇到不可预测性。这些都归结为一件事;有时需要时间将信息输入您的代码可以使用它的处理器。包括获取/解码代码本身所需的时间。
另外,您可以接受多少差异?可能是您对 40 毫秒没问题,或者您对 10 纳秒没问题。
根据应用程序域,您甚至可以进一步掩盖或隐藏差异。多年来,计算机图形人员一直在渲染到屏幕外缓冲区,以隐藏渲染每一帧的时间差异。
传统的解决方案只是尽可能多地删除已知的可变速率事物。将文件加载到 RAM 中,预热缓存并避免 IO。
如果您在关键代码“内联”中进行所有函数调用,并尽量减少您拥有的变量数量,这样您就可以让它们具有“注册”类型。这应该可以提高程序的运行时间。(您可能必须以特殊方式编译它,因为如今的编译器倾向于忽略您的“注册”标签)
我假设当您尝试从内存中加载某些内容时,您有足够的内存不会导致页面错误。页面错误可能需要很长时间。
您还可以查看生成的汇编代码,看看是否有很多分支和内存指令可以更改您的运行代码。
如果在您的代码执行中发生中断,则需要更长的时间。您是否启用了中断/异常?
了解复杂操作的最坏情况运行时并使用计时器。