有时候,理解内置函数的最佳方法是根据它们表示的指令而不是它们描述中给出的抽象语义来理解。
截至今天,完整的本地化常量集如下:
#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
prefetchnta
预读取的是USWC内存区域,它只会使用Line-Fill缓冲区。否则,它会绕过L2缓存并将数据预读取到L1(在具有包容性L3缓存的CPU上也会预读取到L3缓存中)。这是Intel优化手册中所述的。从WB内存执行弱排序加载是不可行的,因为没有办法绕过WB的高速缓存一致性。 - Peter Cordes