在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;
有人能解释一下吗?
缓存线到底是什么?
此外,我猜条件存储是一些汇编指令,它检查某些内容,如果成功,则写入一些值。对吗?
这样的构造什么时候有意义?我想并不总是,因为否则编译器将始终使用条件存储,对吗?
缓存被组织成快速内存块,由于历史原因,这些块被称为行。当您写入缓存行时,它被标记为“脏”,这意味着在缓存控制器硬件中设置了一个位,表示该行需要复制到其他级别的缓存和/或主存储器,然后系统的其他部分才能访问它。
一般来说,内存层次结构的每个级别:寄存器、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
产生一个。
这是否是一个有价值的优化是非常有争议的。条件有自己冲洗指令管道的风险(另一种冲洗)。在没有明确证据表明需要的情况下,永远不要为了速度而牺牲清晰度。
“to cache”的意思是隐藏一些东西。Cache在计算中的作用是隐藏到主存的距离,通过尽可能抢占主存访问。
这只有在您之前使用过数据时才有效,您还没有将其从缓存中推出,并且在您之前没有其他人将其带走。任何其他参与者(其他CPU,IO-Bus,…)必须能够获取当前值并对其进行更改,即使您已将其缓存。这项任务是使用缓存一致性协议完成的。更高的一致性意味着更高的成本。
你的代码试图做的是让编译器发出一个条件移动,所以CPU检查0,只有在不是0的情况下才写入。英特尔/AMD信息系统和许多其他系统中有一整套条件移动指令。
所以现在,一步一步:
那么,值得吗?看情况。
旁白:如果你可以使用条件跳转和存储来合成它们,为什么要提供条件存储指令?优点是使用更少的指令,并且没有刷新指令管道的风险(部分按照指令执行)。更新:看起来它们不能在x86/x86_64上从寄存器/即时移动到内存。