Intel64及IA-32架构优化指南第7章——7.5 与Cache相关的控制

7.5 与Cache相关的控制


本小节涵盖了与Cache相关的控制指令的机制。


7.5.1 非临时存储指令


本小节描述了流存储的行为并重新过一下在前面几个小节中所提到的一些信息。

在流SIMD扩展中,MOVNTPS、MOVNTPD、MOVNTQ、MOVNTDQ、MOVNTI、MASKMOVQ以及MASKMOVDQU指令是流化的,非临时的存储。关于存储器特征以及次序来说,它们类似于写绑定(WC)存储器类型:

●  写绑定——对同一Cache行的相继的写被绑定。

●  写瓦解[译者注:Write Collapsing]——对同一字节的相继的写导致只有最后一次写可见。

●  弱次序——在WC存储之间或WC存储与其它加载或存储之间不保证次序。

●  不可被Cache的以及非写分配——被存储的数据被写在Cache周围并且将不产生对相应Cache行的为所属权而读的总线请求。


7.5.1.1 栅栏


因为流存储是弱次序的,需要一个栅栏操作来确保被存储的数据从处理器冲刷到存储器中。使用一个适当的栅栏失败的话将导致数据在处理器内遭到陷阱,并且将阻止该数据的可见性被其它处理器或系统代理获悉。

WC存储需要软件通过执行栅栏操作来确保数据一致性。见7.5.5小节。


7.5.1.2 流化非临时存储


流存储能通过以下几个方面来提升性能:

●  如果适应在一条Cache行内的64字节被连续地写,那么增加了存储带宽(由于它们不需要为所有权而读的总线请求,而且64字节被绑定到一单个总线写事务)。

●  减少频繁使用的被cache的(临时的)数据的分发(由于它们在处理器Cache周围被写)。

流存储允许对一给定的存储区域的存储器类型的跨混淆。例如,一个区域可使用页属性表(PAT)或存储器类型范围寄存器( MTRRs)被映射为写回(WB),并且然后使用一个流存储来写。


7.5.1.3 存储器类型以及非临时存储


存储器类型能接管一个非临时暗示的优先权,导致以下所要关心的事情:

●  如果程序员对强次序的非可被cache的存储器(比如不可被cache的(UC)或写保护的(WP)存储器类型)指定一个非临时的存储,那么该存储的行为就跟一个不可被cache的存储一样。非临时的暗示被忽略并且那个区域的存储器类型被保留。

●  如果程序员指定了对写绑定的(WC)弱次序不可被cache的存储类型,那么非临时存储和该区域具有相同的语义,并且没有冲突。

●  如果程序员对可被cache的存储器指定了一个非临时的存储(比如写绑定(WB)或通写式的(WT)存储器类型),那么可能导致两种情况:

    ——情况1:如果数据出现在Cache层级中,那么指令将确保一致性。一个特定的处理器可以选择不同的方式来实现这点。这些方法可以是:(a)在维持被指派到那个区域的存储器类型的语义时,在Cache层级内部更新数据;或(b)从Cache逐出数据并将新的非临时数据写到存储器(带有WC语义)。

        这些方法(独立的或绑定的)对于将来的处理器可能是不同的。奔腾4、Intel Core Solo以及Intel Core Duo处理器实现了后者策略(从所有处理器的Cache中逐出数据)。奔腾M处理器实现了两种方法的结合。

        如果流存储命中了出现在第一级Cache的Cache行,那么存储数据被绑定在第一级Cache内部。如果流存储命中了出现在第二级Cache的Cache行,那么该Cache行以及被存储的数据从第二级Cache被冲刷到系统存储器。[译者注:这个机制相应于上一段最后所说的奔腾M处理器的实现。]

    ——情况2:如果数据没有出现在Cache层级内并且目标区域被映射为WB或WT,那么该事务将会是弱次序的并且受所有WC存储器语义的支配。这个非临时的存储将不会写分配。不同的实现可能选择瓦解和绑定这样的存储。


7.5.1.4 写绑定


通常而言,WC语义要求软件确保对于其它处理器以及其它系统代理(诸如图形卡)的一致性。对于执行生产者-消费者使用模型(见7.5.5小节),适当地使用同步以及一个栅栏操作是必须的。栅栏确保所有系统代理对于所存储的数据具有全局的可见性。比如,栅栏失败可能导致一个被写的Cache行待在一个处理器内,而该行将对其它代理不可见。

对于通过更新已经驻留在Cache层级内的数据来实现非临时存储的处理器,应该将目标区域映射为WC。否则的话,如果映射为WB或WT,对于投机处理器会有一个潜在的读将数据带入到Cache中。在这种情况下,非临时存储将会在内部更新并且数据将不会通过后面的栅栏操作从处理器冲刷出去。

在存在存储器类型别名的总线上可见的存储器类型是实现特定的。作为一个例子,写到总线的存储器类型可以反映第一次对该行存储的存储器类型,以程序次序而见。其它可选的方法也可能存在。这种行为应该被考虑为被保留的,并且依赖于未来不兼容的特定实现的风险。


7.5.2 流存储使用模型


对流存储的两个基本的使用领域是一致性的请求和非一致性的请求。


7.5.2.1 一致性的请求


一致性的请求是对系统存储器正常的加载和存储,这个在多处理器环境下也会命中存在另一个处理器中的Cache行。具有一致性的请求,一个流存储可以以用一个WC存储器类型(PAT或MTRR)所映射的一个有规律的存储相同的方式来使用。一条SFENCE指令必须被用在一个生产者-消费者使用模型内,为了确保处理器之间数据的一致性与可见性。

在一单个处理器系统中,CPU也可以对同一存储器位置进行重读并且能担保一致性(即,对这个存储器位置的一单个的,一致的视角)。对于一个多处理器(MP)系统,这同样也是真的,假定采用了一个所接受的MP软件生产者-消费者同步策略。


7.5.2.2 非一致性请求


非一致性的请求发生于一个I/O设备,诸如一个AGP图形卡,使用非一致性的请求来读写系统存储器,这不反映在处理器总线上并从而将不查询处理器的各个Cache。一条SFENCE指令必须在一个生产者-消费者使用模型内被使用,为了确保一致性和处理器之间的数据可见性。这这种情况下,如果处理器在对I/O设备写数据,那么一个流存储可以与具有情况1(7.5.1.2)的任一行为的处理器一起使用,只要该区域也用一个WC存储器类型(PAT,MTRR)来映射。

注:将区域映射为WC失败可能允许该存储器行投机地读到处理器Cache行中(通过一个误预测的分支的错误路径)。

万一该区域没有被映射为WC,那么流可能在Cache内部更新并且后面的一个SFENCE操作将不会导致数据写到系统存储器中。这种情况下显式地将该区域映射为WC确保了从该区域所读取的任一数据将不会被放在处理器的Cache中。一个非I/O设备对该存储器位置的读将返回不正确的/过期的结果。

对于只实现情况2(7.5.1.3小节)的处理器,一个流存储可以在这个非一致性的域被使用,而不需要存储器区域也被映射为WB,由于任一被cache的数据将通过流存储被冲刷到存储器中。


7.5.3 流存储指令描述


MOVNTQ/MOVNTDQ(在一个MMX技术或流SIMD扩展寄存器中对打包整数的非临时存储)将数据从一个寄存器存储到存储器中。它们是隐式弱次序的,不写分配,从而最小化Cache污染。

MOVNTPS(对打包的单精度浮点的非临时存储)类似于MOVNTQ。它从一个流扩展SIMD扩展寄存器以16字节的粒度将数据存储到存储器中。跟MOVNTQ不一样的是,存储器地址必须在16字节边界对齐,否则一个通用保护异常将会发生。该指令是隐式地弱次序的,不写分配,从而最小化了Cache污染。

MASKMOVQ/MASKMOVDQU(在一个MMX技术或流SIMD扩展寄存器中对打包整数的非临时字节掩膜存储)将数据从一个寄存器存储到由EDI寄存器所指定的位置。第二个掩膜寄存器的每个字节中的最高有效位用于基于每个字节选择性地写第一个寄存器的数据。这些指令是隐式弱次序的(即,后继存储不能以原始的程序次序来写存储器),不写分配,从而最小化了Cache污染。


7.5.4 流加载指令


SSE4.1引入了MOVNTDQA指令。MOVNTDQA使用一个非临时暗示从存储器加载16个字节,如果存储器源是WC类型的话。对于WC存储器类型,非临时存储暗示可以通过用与一个对齐的Cache行所等价的一个临时的内部缓存的加载而不把这个数据填充到该Cache中来实现。后面的MOVNTDQA读到被缓存的WC数据的未读部分将引起16字节数据从临时内部缓存传输到一个XMM寄存器中,如果数据可用的话。

如果使用得当,MOVNTDQA可以帮助软件实现比起其它方法来高得多的吞吐,当在WC存储器区域中将数据加载到处理器时。

第一章提供了对一个应用程序使用MOVNTDQA的注解参考。其它信息以及适当使用MOVNTDQA的要求可以在第12章找到。


7.5.5 FENCE指令


下列栅栏指令是可用的:SFENCE、IFENCE以及MFENCE。


7.5.5.1 SFENCE指令


SFENCE(存储FENCE)指令使得对于每个在一个SFENCE之前的STORE指令,在跟在这个SFENCE之后的任一存储之前,以程序次序全局可见。SFENCE提供了确保产生弱次序结果的两个例程之间的次序的一个高效的方法。

弱次序存储器类型的使用在某些数据共享关系下可能比较重要(诸如生产者-消费者关系)。使用弱次序存储器能使得汇编数据更加高效,但必须注意确保消费者所获得的数据是生产者所想看到的。

一些公共使用模型可能受弱次序存储的影响。这些例子有:

●  使用弱次序存储器来写结果的库函数

●  也可能从写弱次序结果获利的编译器所生成的代码

●  手写的代码

对于不同的情况,一个消费者数据知道数据弱次序的程度也是有所不同的。因而,SFENCE应该被用于确保产生弱次序的数据的例程与消费该数据的例程之间的次序。


7.5.5.2 LFENCE指令


LFENCE(加载栅栏)指令使得在LFENCE指令之前的每个加载指令,在跟在LFENCE之后的任一加载指令之前,以程序次序全局可见。

LFENCE指令提供了将加载指令从其它加载分离的一种手段。


7.5.5.3 MFENCE指令


MFENCE(存储器栅栏)指令使得对于在MFENCE指令之前的每个加载/存储指令,在任一跟在MFENCE之后的加载/存储之前,以程序次序全局可见。MFENCE提供了从其它存储器引用分离某个存储器指令的一个手段。

对一个LFENCE与SFENCE的使用并不等价于对一个MFENCE的使用,由于加载和存储栅栏它们相互之间没有次序。换句话说,加载栅栏可以在先前的存储之前被执行而存储栅栏可以在先前的加载之前被执行。

MFENCE应该在每当Cache行冲刷指令(CLFLUSH)被用于确保由处理器所生成的对存储器的投机引用不干涉冲刷时所使用。见7.5.6小节,“CLFLUSH指令”。


7.5.6 CLFLUSH指令


CLFLUSH指令无效化在所有等级的处理器Cache层级中(数据和指令),与包含存储器位置的字节地址的线性地址相关联的Cache行。该无效化是通过一致性域的广播。在任一Cache层级的级上,如果一个行与存储器不一致(脏了),那么它在无效化之前被写到存储器。其它特征包括:

●  所影响的数据大小是Cache一致性的大小,这在奔腾4上是64个字节。

●  包含所受影响的行的页的存储器属性对于该条指令的行为没有影响。

●  CLFLUSH指令可在所有特权等级上所使用,并且经受所有准许检查和与一个字节加载相关联的错误。

CLFLUSH是一个对其它存储器交通来说不按次序的操作,包括其它的CLFLUSH指令。在需要关心次序的时候软件应该使用一个存储器栅栏。

作为一个例子,考虑一个视频使用模型,一个视频捕获设备在使用一个非一致性的AGP访问将一个捕获流直接写入系统存储器。由于这些非一致的写没在总线上广播,因而它们将不会冲刷驻留在处理器Cache中的同一位置的拷贝。结果,在处理器重新读捕获缓存之前,它应该使用CLFLUSH以确保捕获缓存的陈旧的拷贝从处理器Cache被冲刷出来。由于可能被处理器所生成的投机的读,(使用MFENCE)来观察适当的栅栏。

例7-1提供了对CLFLUSH用法的伪代码。

while(!buffer_ready){}
mfence
    for(i=0; i<num_cachelines; i+=cacheline_size){
        clflush((char*)((unsigned int)buffer + i))
    }
mfence
    prefnta buffer[0];
    VAR = buffer[0];

 

posted @ 2013-03-28 13:13  zenny_chen  Views(1517)  Comments(0Edit  收藏  举报