如何确保代码运行不会因缓存而在执行时间上发生变化?

时间:2020-03-05 18:55:03  来源:igfitidea点击:

在具有严格实时限制的嵌入式应用程序(在32位处理器上用C语言编写)中,关键代码(特别是中断)的执行时间必须恒定。

我们如何确保在执行代码时不会引入时间可变性,特别是由于处理器的高速缓存(L1,L2或者L3)引起的时间可变性?

请注意,由于它对执行速度产生巨大影响(有时与访问RAM相比,有时会超过100:1),因此我们担心缓存行为。由于特定的处理器体系结构而引入的可变性远不及高速缓存的大小。

解决方案

回答

两种可能性:

完全禁用缓存。该应用程序将运行较慢,但没有任何可变性。

将代码预加载到缓存中并"锁定"。大多数处理器都提供了执行此操作的机制。

回答

预分配内存,并确保中断不会影响缓存(不可能,正确)。

/艾伦

回答

看来我们指的是x86处理器系列,它不是为实时系统而构建的,因此不能真正保证恒定时间执行(CPU可能会对微指令重新排序,而分支预测和指令预取队列会每次CPU错误地预测条件跳转时都会刷新...)

回答

了解复杂操作的最坏情况运行时并使用计时器。

回答

这个答案听起来似乎很愚蠢,但目的是让我们思考:

Only run the code once.

我之所以这么说是因为太多的事情会使它变得可变,而我们甚至可能无法控制它。我们对时间的定义是什么?假设操作系统决定将进程放入等待队列。

接下来,由于缓存性能,内存延迟,磁盘I / O等原因,我们将无法预测。这些全都归结为一件事。有时需要花费一些时间才能将信息送入代码可以使用的处理器中。包括获取/解码代码本身所需的时间。

另外,我们可以接受多少差异?可能是40毫秒就可以,或者10纳秒就可以。

根据应用程序域,我们甚至可以进一步掩盖或者隐藏差异。多年来,计算机图形学人员一直在渲染到屏幕外的缓冲区,以隐藏渲染每一帧时间的差异。

传统解决方案只是消除了尽可能多的已知可变速率事物。将文件加载到RAM中,预热缓存并避免IO。

回答

如果我们在关键代码"内联"中进行所有函数调用,并最大程度地减少了拥有的变量数量,则可以使它们具有"注册"类型。
这样可以改善程序的运行时间。 (我们可能必须以一种特殊的方式进行编译,因为如今的编译器往往会忽略'register'标签)

我假设我们有足够的内存,当我们尝试从内存中加载某些内容时,不会引起页面错误。页面错误可能会花费很多时间。

我们还可以查看生成的汇编代码,以查看是否有很多分支和内存指令可以更改正在运行的代码。

如果代码执行中发生中断,则将需要更长的时间。我们是否启用了中断/异常?

回答

如果我们可以使用硬件,或者可以与他人合作,则可以关闭缓存。某些CPU的引脚如果连接到地而不是接地(或者相反),则会禁用所有内部缓存。这将提供可预测性,但不会带来速度!

失败的话,也许可以在软件的某些地方编写代码,以故意用垃圾填充高速缓存,因此可以保证接下来发生的一切都是高速缓存未命中。如果做得对,可以提供可预测性,也许只能在某些地方完成,所以速度可能比完全禁用缓存要好。

最后,如果速度确实很重要,请仔细设计软件和数据,就像在为古老的8位CPU编程的过去一样,将其保持足够小以使其全部适合L1缓存。这些天来,板载缓存的容量比过去(最低十年)的小型计算机上的所有RAM都要大,我总是感到惊讶。但这将是艰苦的工作,并且需要聪明。祝你好运!