GCC doc here指定_buitin_prefetch的用法。
第三个论点是完美的。 如果为0,则编译器生成prefetchtnta(%rax)指令 如果为1,则编译器生成prefetcht2(%rax)指令 如果是2,则编译器生成prefetcht1(%rax)指令 如果它是3(默认值),编译器将生成prefetcht0(%rax)指令。
如果我们改变第三个参数,操作码已经相应地改变了。
但第二个论点似乎没有任何效果。
__builtin_prefetch(&x,1,2);
__builtin_prefetch(&x,0,2);
__builtin_prefetch(&x,0,1);
__builtin_prefetch(&x,0,0);
以上是生成的代码示例:
以下是集会:
27: 0f 18 10 prefetcht1 (%rax)
2a: 48 8d 45 fc lea -0x4(%rbp),%rax
2e: 0f 18 10 prefetcht1 (%rax)
31: 48 8d 45 fc lea -0x4(%rbp),%rax
35: 0f 18 18 prefetcht2 (%rax)
38: 48 8d 45 fc lea -0x4(%rbp),%rax
3c: 0f 18 00 prefetchnta (%rax)
可以观察第3个参数的操作码变化。但即使我更改了第二个参数(指定读或写),汇编代码仍保持不变。 < 27,2a>和< 2e,31>。所以它没有向机器提供任何信息。那么第二个论点的目的是什么?
答案 0 :(得分:4)
您发布的同一链接:
有两个可选参数, rw 和 locality 。 rw 的值是编译时常量1或0; 一意味着预取正在准备写入内存地址,零是默认值,意味着预取正准备读取。
x86架构在读取和写入预取之间没有区别 这并不意味着您应该忽略第二个参数,因为在C中编写代码是为了提高可移植性。 即使在您的机器中没有使用第二个参数,也可以在编译到不同的体系结构时使用它。
修改强>
正如@PeterCordes在他的评论中指出的那样,x86实际上有一个预取指令,可以预期写入。
它与其他预取指令不同,因为它使获取的行的其他缓存实例无效(并将其设置为独占状态)。
答案 1 :(得分:4)
玛格丽特指出,其中一个是 rw 。
基线x86-64(SSE2)不包括写入预取指令,但它们作为ISA扩展存在。像往常一样,除非你告诉他们你正在编译支持它的目标,否则编译器不会使用它们。 (但它们可以安全地在任何非古老的CPU上作为NOP运行。)
这两条指令是:PREFETCHW(进入L1d缓存,如PREFETCHT0)和PREFETCHWT1(进入L2缓存,如PREFETCHT1)。 他们通过发送RFO(Read-For-Ownership)预取一行进入独家MESI状态。这会使每个其他核心中的所有其他副本无效。从该状态,存储缓冲区可以将数据提交到一行(并将其翻转为已修改),而无需任何进一步的非核心流量。或者如果在驱逐前没有修改,可以简单地删除。
PREFETCHW指令仅仅是一个提示,不会影响程序行为。如果执行,该指令会将数据移近处理器,并使其他缓存副本无效,以防将来写入该行。
它们具有几乎相同的机器编码,相同的OF 0D
操作码,仅在ModRM /1
字段中的/2
或/r
不同。就像读取预取PREFETCHT0 / T1 / T2 / NTA如何共享操作码一样,并且仅由/0
(NTA),/1
(T0)等在ModRM /r
字段中区分。使用/r
位作为额外的操作码位不是唯一的;其他单操作数和即时指令也可以。
相关:Difference between prefetch for read or write
PREFETCHW最初出现在AMD's 3DNow!中,但有自己的功能位,以便CPU可以指示支持它而不是其他3DNow! (打包 - MMX注册中的float
)说明。
PREFETCHWT1也有自己的CPUID功能位,但可能与AVX512PF相关联。它似乎只能在Xeon Phi(Knight's Landing / Knight's Mill)中使用,而不是主流的Skylake-AVX512,与AVX512PF(https://en.wikipedia.org/wiki/AVX-512#CPUs_with_AVX-512)相同。 (证据:根据Intel's Future Extensions manual,EID = 7 / ECX = 0的CPUID在ECX中提供了一个功能位图,包括 Bit 00:PREFETCHWT1(仅限英特尔®至强融核™)。另外{{ 3}}。
__builtin_prefetch(p,1,2);
使用GCC编译如下:
-m
选项,或-march=haswell
或更早的英特尔。-march=k8
或-march=bdver2
(打桩机)。-march=broadwell
或更新的英特尔SnB系列进行PREFETCHW,和/或-mprfchw
用于任何拱门。 PREFETCHWT1 mailing list。 (如果PREFETCHW也可用,gcc将其用于locality = 3,但PREFETCHWT1用于locality< = 2.。)由于某种原因,GCC不会将此作为-march=knl
或-march=knm
的一部分启用,但是clang确实。我认为这是海湾合作委员会的疏忽。
-mprefetchwt1
隐含-mprfchw
。另请参阅GCC手册中的-mprefetchwt1
部分,了解有关-march=native
与-march=whatever
的更多信息,以启用一组ISA扩展并正确设置-mtune=whatever
。
在x86 options上查看-march=haswell
与-march=broadwell -mprefetchwt1
。或者自己修改编译器args。
clang -O3 -march=knl
和gcc -O3 -march=broadwell -mprefetchwt1
制作相同的作品:
pref:
prefetchwt1 [rdi] # __builtin_prefetch(p,1,2); // KNL only, otherwise we get prefetchw
prefetchw [rdi] # __builtin_prefetch(p,1,3);
prefetcht0 [rdi] # __builtin_prefetch(p,0,3);
prefetcht1 [rdi] # __builtin_prefetch(p,0,2);
prefetcht2 [rdi] # __builtin_prefetch(p,0,1);
prefetchnta [rdi] # __builtin_prefetch(p,0,0);
ret
另请注意,他们的Godbolt compiler explorer在非古老的CPU上没有PREFETCHW或3DNow!功能位。在早期的64位Intel CPU上,这是一个非法指令。 (较新版本的Windows要求PREFETCHW在没有故障的情况下执行,在这种情况下,人们会谈论CPU“支持PREFETCHW”,即使它作为NOP运行)。
支持PREFETCHW但不支持PREFETCHWT1的CPU实际上可能会运行PREFETCHWT1,就像它是PREFETCHW一样,但我还没有测试过。 (它应该可以通过在不同内核上运行线程来测试,一个执行重复存储到一个位置,另一个执行PREFETCHWT1与PREFETCHW相比读取预取与NOP,并查看写入线程的吞吐量如何受到影响。)
最好使用读取意图预取而不是NOP(如GCC那样)。但是你可能不想做PREFETCHW和PREFETCHT0,因为太多的预取指令并不是一件好事。 (特别是英特尔IvyBridge,它对预取指令吞吐量有一些性能缺陷。但IvB会将PREFETCHW作为NOP运行,因此你只能在该uarch上获得一个预取。)
调整软件预取很难:如果HW预取成功完成其工作,则预取太多意味着执行实际工作所花费的执行资源会减少。请参阅0F 0D r/m8
machine code decodes as a multi-byte NOP和Cost of a sub-optimal cacheline prefetch