Event | Action
-------------------+----------------------------------------------
Read hit | Read from the cache line
-------------------+----------------------------------------------
Read miss | Fill the line then read from the cache line
-------------------+----------------------------------------------
Write hit | Write to the cache line
-------------------+----------------------------------------------
Write miss | Fill the line then write to the cache line*
* Since P6, we will assume this is always the case
Legend
LF = Line Fill; LR = Read from line; MR = Read from memory
LW = Write to line; MW = Write to memory
WB = Write back; UC = Uncachable (Not cached);
WT = Write through; WP = Write protect
Event | WB | UC | WT | WP |
-------------------+---------+-----+----------+--------+----------
Read hit | LR | MR* | LR | LR |
-------------------+---------+-----+----------+--------+----------
Read miss | LF, LR | MR | LF, LR | LF, LR |
-------------------+---------+-----+----------+--------+----------
Write hit | LW | MW | LW, MW** | MW*** |
-------------------+---------+-----+----------+--------+----------
Write miss | LF, LW | MW* | MW | MW |
* A hit on a UC region can happen if the cache type has been
changed without invalidating the cache. In truth for UC talking about
hits is a bit misleading. Caching is bypassed, so it is actually a
N.A. case.
** The line can be invalidated as the result of the eviction operation
used.
*** Evict the line, even if it was Modified or Exclusive, e.g. because of
writes via other current or previous mapping for the same physical page.
Writing directly to memory by bypassing the caches also invalidates
all the copies of the affected line in other processors.
备注: WP和WT之间的区别在于后者可以被视为“穿透”缓存,而前者则“绕过”缓存。
写合并
WC实际上不是一种缓存策略,但由于它与缓存策略紧密耦合,因此在这里讨论。
我发现英特尔SDM在这个主题上非常混乱,这是我目前对此事的解释。
欢迎纠正!
有一篇来自英特尔的旧论文可在这里找到关于WC的内容已经包含在Intel SDM 3的第11.3.1节中,但在这样做时,英特尔失去了原始文件中可用的一些上下文和结构。
WC的想法是在长时间操作(如总线事务或高速缓存一致性事务)之前或期间合并写入。
为了实现这一点,处理器拥有多个 WC缓冲区 - 不要将其与缓存行的存储缓冲区混淆。
WC缓冲区是完全独立的实体!
WC缓冲区的数量是有限制的:在P6(因此在Pentium M)中有6个WC缓冲区,Pentium 4有8个,自Nehalem以来有10个WC缓冲区请参见英特尔优化手册的第3.6.10节。
每个WC缓冲区的大小没有架构定义,但到目前为止,它总是与缓存行的大小相同(因此对于P6为32字节,对于其他缓存为64字节)。
WC缓冲区位于高速缓存之前。
现在问题来了,WC实际上有两个含义:
在写缺失的情况下,在CPU被允许写入缓存行之前,必须执行RFO(读取所有权)以通知其他CPU它们缓存的数据是否无效。
由于这可能需要很长时间,CPU会将存储器暂存在WC缓冲区中,并继续进行其工作。然后,缓存子系统将自主地将WC缓冲区移动到适当的缓存中。
如果连续一行正在执行RFO的存储器抵达,则它们都将沉在WC缓冲区中。
如果内存类型不涉及缓存,则WC缓冲区可以充当存储器的停车场,直到一起传输到总线。
启动总线事务需要一些开销,但一旦它开始,数据就可以相继传输(实际上每次传输8个字节),非常快。
写入比内存总线宽度少的数据是浪费内存带宽 - 就像让一辆(真正的)公共汽车半空驶过去一样。
因此,WC试图利用完整的内存总线宽度。
备注:UC内存类型绕过缓存子系统,包括WC缓冲区。WC内存类型绕过缓存但不绕过WC缓冲区。
在第一种情况下,WC允许CPU在长时间操作进行时继续工作,在第二种情况下,它允许数据的高效传输。编辑
我忘了提到WC内存类型是不可缓存的(但是具有推测性,推测性已被有意忽略)。 彼得·科德斯在他的评论中完美地概括了它:
WC是USWC的简写:“不可缓存的推测性写组合”。 关键点在于它是一种不可缓存的内存类型,这就是为什么
movntdqa
负载很有用(以加载一个[存储缓冲区条目,它与一个]完整的缓存行相同,而不是针对来自相同缓存行的单独访问DRAM进行分离的负载[驻留在可以缓存的地址范围内])
我只是添加了括号中的文本,因为我认为他使用“缓存行”作为度量单位和内存地址的区分属性。
在任何时刻只能有四个WC缓冲区处于活动状态。
Intel优化手册还报告说,只能使用两个缓冲区来合并写入高速缓存的操作。
当使用WC将存储器中的存储合并时,处理器不强制执行一致性。
WC缓冲区不被嗅探,此外,当前活动的WC缓冲区之间没有强制执行的顺序(哪一个首先分裂)。
当必须逐出WC时,如果WC缓冲区全部是脏的,则通过执行单个总线事务进行;如果WC缓冲区包含部分数据,则可以通过执行多达8个(64/8)或4个(32/8)总线事务来完成逐出。
同样,在溢出具有部分数据的WC缓冲区时,这些块之间也没有强制执行的顺序。
P6会在软件写入缓冲区大小之外的位置时立即逐出WC缓冲区,确切的算法是由实现定义的 - 通常有一个时间窗口允许软件合并写入操作。
可以通过访问UC内存类型区域或特定事件(请参见本节开头链接的论文)来强制逐出。
movntdqa
加载很有用(可以加载整个缓存行,而不是从同一缓存行分别访问DRAM进行单独的加载)。 - Peter Cordes