小编D. *_*per的帖子

(非)确定性 CPU 行为以及(物理)执行持续时间的推理

过去我曾处理过时间紧迫的软件开发。这些应用程序的开发基本上是这样进行的:“让我们编写代码,测试延迟和抖动,并对两者进行优化,直到它们处于可接受的范围内。” 我觉得这非常令人沮丧;这不是我所说的正确的工程,我想做得更好。

所以我研究了这个问题:为什么我们会有抖动?答案当然是:

  • 缓存:从主内存获取一段代码或数据比从 L1 缓存获取相同数据要多花费 2 个数量级的时间。所以物理执行时间取决于缓存中的内容。这又取决于几个因素:
    • 应用程序的代码和数据布局:我们都知道可怕的行主与列主矩阵遍历示例
    • CPU 的缓存策略,包括缓存行的推测性预取
    • 同一核心上的其他进程正在做事情
  • 分支预测:CPU 尝试猜测条件跳转的哪个分支将被执行。即使相同的条件跳转执行两次,预测也可能不同,因此一次可能会形成“管道泡沫”,而另一次则不会。
  • 中断:异步行为显然会导致抖动
  • 频率缩放:幸好在实时系统中被禁用

有很多事情会干扰一段代码的行为。尽管如此:如果我有两条指令,位于同一缓存行,不依赖于任何数据,也不包含(条件)跳转。然后,应该消除缓存和分支预测带来的抖动,并且只有中断才能发挥作用。正确的?好吧,我编写了一个小程序,获取时间戳计数器 (tsc) 两次,并将差异写入标准输出。我在一个打了 rt 补丁的 Linux 内核上执行了它,并且禁用了频率缩放。

该代码具有基于 glibc 的初始化和清理,并调用 printf,我认为它有时在缓存中,有时不在缓存中。但是在调用“rdtsc”(将 tsc 写入 edx:eax)之间,二进制文件的每次执行都应该是确定性的。为了确定起见,我反汇编了 elf 文件,这里是两个 rdtsc 调用的部分:

00000000000006b0 <main>:
 6b0:   0f 31                   rdtsc  
 6b2:   48 c1 e2 20             shl    $0x20,%rdx
 6b6:   48 09 d0                or     %rdx,%rax
 6b9:   48 89 c6                mov    %rax,%rsi
 6bc:   0f 31                   rdtsc  
 6be:   48 c1 e2 20             shl    $0x20,%rdx
 6c2:   48 09 d0                or     %rdx,%rax
 6c5:   48 29 c6 …
Run Code Online (Sandbox Code Playgroud)

cpu time caching deterministic

7
推荐指数
1
解决办法
1344
查看次数

标签 统计

caching ×1

cpu ×1

deterministic ×1

time ×1