cmpxchg 是否会在失败时写入目标缓存行?如果不是,对于自旋锁来说它比 xchg 更好吗?

2023-12-03

我假设简单的自旋锁不会进入操作系统等待这个问题的目的。

我发现简单的自旋锁通常使用以下方式实现lock xchg or lock bts代替lock cmpxchg.

但没有cmpxchg如果期望不匹配,避免写入值?那么失败的尝试不是更便宜吗cmpxchg?

Or does cmpxchg即使发生故障,也要写入数据并使其他核心的缓存行无效吗?

这个问题类似于具体是什么将 x86 缓存行标记为脏 - 任何写入,或者是否需要显式更改?,但它特定于cmpxchg,不是一般情况下。


在大多数或所有当前的 Intel x86 处理器上,lock cmpxchg到内存类型为 WB 且完全包含在单个 L1D 缓存行中的位置执行如下:

  • 向 L1D 发出锁定读取请求,使目标行处于锁定独占高速缓存一致性状态,并将请求的字节作为输入提供给执行端口之一以执行比较。 (从 P6 开始支持高速缓存锁定。)处于锁定状态的行不能因任何原因而失效或被逐出。
  • 执行相等比较。
  • 无论结果是什么,向 L1D 发出解锁写入请求,这会将缓存行的状态更改为“已修改”并解锁该行,从而允许其他访问或一致性请求替换该行或使该行无效。

可以使用某些性能事件或基于延迟的测量来凭经验观察第一步和最后一步。一种方法是分配一大堆原子变量,然后执行lock cmpxchg在该数组上循环。锁读请求类型是RFO请求类型之一。所以L2_TRANS.RFO事件(或等效事件)在大多数微架构上都是可靠的,可用于测量 L2 的锁读取次数。 (L2_TRANS.RFO计数需要 RFO,因此最好关闭硬件预取器以避免 L2 中不需要的命中。这也适用于L2_RQSTS.RFO_*.)

还有一些用于衡量写回次数的事件,例如L2_TRANS.L1D_WB, L2_TRANS.L2_WB, 和别的。不幸的是,许多这些事件和跨许多微体系结构要么计数不足,要么计数过多,或者它们计数准确但不一定是所有/仅脏缓存行写回。因此,他们更难以推理,而且通常不可靠。

更好的方法是执行lock cmpxchg在特定物理核心上的阵列的一个部分上,然后将线程迁移到另一个物理核心(在同一 L3 共享域中)并执行一个循环,在该循环中读取该部分的元素(正常读取)。如果lock cmpxchg指令将目标行置于 M 状态,来自同一 L3 共享域中的另一个物理核心的读取请求应该在 L3 中命中,并且也会在该核心的私有缓存中命中修改。lock cmpxchg被处决。这些事件可以使用以下方式进行计数OFFCORE_RESPONSE.DEMAND_DATA_RD.L3_HIT.HITM_OTHER_CORE(或等效的),这在大多数/所有微架构上都是可靠的。

锁定指令是一项昂贵的操作,原因有以下三个:(1) 需要使行处于独占状态,(2) 使行变脏(可能不必要),并且太多的写回会对执行时间产生重大影响,甚至更是如此当它们最终从长时间的读取请求中窃取主内存带宽时,当写入持久内存时更是如此,并且(3)它们在架构上进行序列化,这使得指令位于关键路径上。

英特尔有一个patent这提出了对最后一个的优化,其中核心乐观地假设不存在锁争用并向目标线发出推测性正常负载。如果该线路不存在于任何其他物理核心中,则该线路在请求核心中将处于独占状态。然后,当锁定指令执行并发出锁定读取请求时,该行有望仍处于独占状态,在这种情况下,锁定指令的总延迟将减少。我不知道是否有处理器实现了这种优化。如果实施的话,数量L2_TRANS.RFO事件将远小于锁定的行数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

cmpxchg 是否会在失败时写入目标缓存行?如果不是,对于自旋锁来说它比 xchg 更好吗? 的相关文章

随机推荐