12

libuv 源码中,我找到了这段代码:

  /* The if statement lets the compiler compile it to a conditional store.
   * Avoids dirtying a cache line.
   */
  if (loop->stop_flag != 0)
    loop->stop_flag = 0;

有人可以解释一下吗?

缓存线到底是什么?

另外,我猜条件存储是一些汇编程序指令,它检查某些内容,如果成功,则写入一些值。正确的?

这种结构什么时候有意义?我想并非总是如此,否则编译器只会使用条件存储,对吧?

4

2 回答 2

7

高速缓存以快速内存块的形式组织,由于历史原因,这些块被称为行。当您写入缓存行时,它被标记为“脏”,这意味着在缓存控制器硬件中设置了一个位,表示该行需要在其他部分之前复制到其他级别的缓存和/或主内存系统可以访问它。

通常,内存层次结构的每一级:寄存器、L1、L2、L3 ... 高速缓存、主内存和交换空间都具有相同信息的不同副本。确保系统的不同部分(处理器、DMA、视频子系统等)看到相同的值,即使一个或多个副本可能已更改,这称为一致性问题

一般的解决方案是暂停以将更新的值复制到层次结构的不同级别。这称为冲洗

刷新可能会花费 10 到 - 在最坏的情况下会导致页面错误 - 可能是数百万个处理器周期。

由于成本高昂,硬件设计人员不遗余力地尽量减少对冲洗的需求。在这里,程序员也承担了责任。

评论是说“如果缓存已经在标志中包含一个零,我们不要在零上写一个零,因为这会将缓存行标记为脏,这可能会导致不必要的刷新。”

“条件存储”是一个有点晦涩的术语。它只是指在正常存储周围的零跳转,这是编译器将从if语句中生成的代码。在 X86 中,它看起来像:

    ;; assume edi holds value of pointer 'loop'
    ;; and flag is a constant offset for the 'stop_flag' field.
    cmp dword ptr [edi, flag], 0
    jz no_store
    mov [edi, flag], 0
no_store:
   ... code continues

如果缺少 if 语句,您将只有最后一条mov指令。

NB一位评论者指出,在重要的处理器架构上确实存在单一的“条件移动/存储”指令。我还没有看到gcc生产一个。

这是否值得优化是非常值得商榷的。条件语句有自己的刷新指令流水线的风险(一种不同的刷新)。永远不要在没有明确证据表明需要的情况下牺牲清晰度来换取速度。

于 2014-03-28T15:36:53.250 回答
1

“缓存”意味着隐藏某些东西。Cache在计算中的作用是通过尽可能抢占主存访问来隐藏与主存的距离。

这只适用于您之前使用过数据,您还没有将其从缓存中推出,并且没有其他人在您之前将其取走。任何其他参与者(其他 CPU、IO-Bus,...)必须能够获取当前值并更改它,即使您已将其缓存。此任务是使用缓存一致性协议完成的。更高的一致性意味着更高的成本。

您的代码试图做的是让编译器发出条件移动,因此 CPU 检查 0,如果不是 0,则只写入。在 Intel/AMD IS 和许多其他指令中有一整套条件移动指令。

所以现在,一步一步:

  • 测试 0:如果你的 CPU 没有测试数据的副本,它必须要求一个。这比以前差多了。让我们希望你不要碰到主内存。
  • 准备写一个值:
    • 您拥有数据:太好了,您已经完成了。
    • 你不拥有数据:缓存调用它的兄弟和更高层来通知他们它现在拥有这块。没有其他人可以保留副本。
  • 写一个值:缓存保存更改并标记缓存行(缓存的最低粒度)脏,需要写回。

那么,值得吗?这取决于。

旁白:如果您可以使用条件跳转和存储来综合它们,为什么要提供条件存储指令?优点是使用更少的指令并且没有刷新指令流水线的风险(部分在指令之后执行)。更新:看起来他们无法在 x86/x86_64 上从寄存器/立即数移动到内存。

于 2014-03-28T16:50:28.770 回答