什么是_mm_prefetch()本地性提示?

31
内部函数指南 对于 void _mm_prefetch (char const* p, int i) 只有这样的描述:

将包含地址为 p 的数据行从内存中提取到由局部性提示 i 指定的缓存层次结构中的位置。

你可以列出 int i 参数的可能值并解释它们的意义吗?

我找到了 _MM_HINT_T0, _MM_HINT_T1, _MM_HINT_T2, _MM_HINT_NTA_MM_HINT_ENTA,但我不知道这是一个穷尽列表以及它们的含义。

如果是处理器特定的,我想知道它们在 Ryzen 和最新的 Intel Core 处理器上的作用。

1个回答

45
有时候,理解内置函数的最佳方法是根据它们表示的指令而不是它们描述中给出的抽象语义来理解。

截至今天,完整的本地化常量集如下:
#define _MM_HINT_T0 1
#define _MM_HINT_T1 2
#define _MM_HINT_T2 3
#define _MM_HINT_NTA 0
#define _MM_HINT_ENTA 4
#define _MM_HINT_ET0 5
#define _MM_HINT_ET1 6
#define _MM_HINT_ET2 7

正如这篇介绍英特尔Xeon Phi协处理器预取功能的论文所描述的。

对于IA32/AMD处理器,该集合会被缩小为

#define _MM_HINT_T0 1
#define _MM_HINT_T1 2
#define _MM_HINT_T2 3
#define _MM_HINT_NTA 0
#define _MM_HINT_ET1 6

_mm_prefetch 根据架构和局部性提示编译成不同的指令。

    Hint              IA32/AMD          iMC
_MM_HINT_T0           prefetcht0     vprefetch0
_MM_HINT_T1           prefetcht1     vprefetch1
_MM_HINT_T2           prefetcht2     vprefetch2
_MM_HINT_NTA          prefetchnta    vprefetchnta
_MM_HINT_ENTA              -         vprefetchenta
_MM_HINT_ET0               -         vprefetchet0
_MM_HINT_ET1          prefetchwt1    vprefetchet1
_MM_HINT_ET2               -         vprefetchet2

(v)prefetch指令在满足所有要求的情况下,可以将一行缓存数据预取到特定级别的缓存中。
该指令只是一种提示,可能会被忽略。

当一行数据被预取到X级时,手册(Intel和AMD)都说明它也会被预取到所有更高级别的缓存中(但对于X=3的情况除外)。
我不确定这是否真实,我认为这行数据是相对于X级缓存进行预取的,并且根据更高级别的缓存策略(包容性与非包容性)可能存在或不存在于那里。

(v)prefetch指令的另一个属性是非暂态属性。
非暂态数据不太可能很快被重用。
在我看来,NT数据存储在IA32架构的“流式加载缓冲区”中1,而对于iMC架构,则存储在正常缓存中(使用硬件线程ID作为路径),但采用最近使用替换策略(以便如果需要,它将是下一个被驱逐的行)。
对于AMD,手册阅读实际位置是实现相关的,范围从软件不可见缓冲区到专用非暂态缓存。

(v)prefetch指令的最后一个属性是“意图”属性或“驱逐”属性。
由于MESI和变体协议,必须发出所有权请求才能将一行数据带入独占状态(以便修改它)。
RFO只是一种特殊读取,因此使用RFO预取它将直接将其带入独占状态(否则,第一个对其进行的存储将取消预取的好处,因为需要“延迟”的RFO),前提是我们知道稍后会对其进行写入。

IA32和AMD架构尚不支持独占非暂态提示(但iMC架构允许使用局部性代码_MM_HINT_ENTA)。

1 我理解的是WC缓冲区。Peter Cordes在下面的评论中澄清了这一点:“如果预取USWC内存区域,则prefetchnta仅使用Line-Fill缓冲区。否则,它会预取到L1中。”


以下是涉及的指令描述,供参考

PREFETCHh

从包含源操作数指定字节的内存中获取数据行,并将其预取到由局部性提示指定的缓存层次结构中:

• T0(暂态数据) - 将数据预取到所有缓存层次结构中。
• T1(相对于一级缓存未命中的暂态数据) - 将数据预取到二级缓存及更高级别的缓存中。
• T2(相对于二级缓存未命中的暂态数据) - 将数据预取到三级缓存及更高级别的缓存中,或选择实现特定选项。
• NTA(相对于所有缓存级别的非暂态数据) - 将数据预取到非

                 Cache  Temporal    Exclusive state
                 Level
VPREFETCH0       L1     NO          NO
VPREFETCHNTA     L1     YES         NO
VPREFETCH1       L2     NO          NO
VPREFETCH2       L2     YES         NO
VPREFETCHE0      L1     NO          YES
VPREFETCHENTA    L1     YES         YES
VPREFETCHE1      L2     NO          YES
VPREFETCHE2      L2     YES         YES

6
如果prefetchnta预读取的是USWC内存区域,它只会使用Line-Fill缓冲区。否则,它会绕过L2缓存并将数据预读取到L1(在具有包容性L3缓存的CPU上也会预读取到L3缓存中)。这是Intel优化手册中所述的。从WB内存执行弱排序加载是不可行的,因为没有办法绕过WB的高速缓存一致性。 - Peter Cordes
谢谢@PeterCordes,这是一个非常有用的评论! - Margaret Bloom
5
抱歉,我之前的评论并不完全准确。NT 存储确实在WB内存上绕过了高速缓存一致性。(弱排序有点类似于绕过一致性。从WB内存中进行弱排序加载是不可能的,但是据说prefetchNTA可以减少高速缓存污染。另外,英特尔的手册还指出,如果prefetchNTA将数据放入L3中,则仅在任何给定集合中的一个路径中进入,因此仍然可以减少污染。我有一个未完成的答案,其中包含更多详细信息,我应该完成并发布...) - Peter Cordes
@PeterCordes,非常有趣。 我期待你的回答! - Margaret Bloom
3
否则,第一个存储访问将由于需要“延迟”的RFO而取消预取带来的好处。实际上,情况通常没有那么糟糕。除非该行实际上是共享的,否则它将以E状态进入核心,因此第一个写操作将必须执行E -> M转换,但这是便宜的并且通常是“本地的”(即,核心只需要在其私有缓存之一(L1或L2)中翻转一个位,因此它与对内存或共享缓存的未命中完全不同。从这个意义上讲,最初的请求是否被“正确”标记为RFO,对于实际上是共享的行来说更重要。 - BeeOnRope
再次更正我的上一条评论:NT 存储器绕过缓存,但不绕过一致性:它们仍会使其他核心可能拥有的任何行的副本无效,甚至在允许从存储缓冲区提交到 LFB 之前就进行无效操作。(也许是针对脏数据的写回;他们必须避免在 NT 存储器对所有核心可见之前从其他核心回滚存储。)它们的一致性流量只是无效操作,而不是 RFO。 (相关:增强的 REP MOVSB 用于 memcpy 关于无 RFO 存储协议。)再次强调,这与 NT 加载无关。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接