_builtin_prefetch()中第二个参数的影响是什么?

时间:2016-11-09 18:02:54

标签: c gcc assembly x86 prefetch

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>。所以它没有向机器提供任何信息。那么第二个论点的目的是什么?

2 个答案:

答案 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编译如下

  • PREFETCHT1没有-m选项,或-march=haswell或更早的英特尔。
  • PREFETCHW与AMD目标,如-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=knlgcc -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 NOPCost of a sub-optimal cacheline prefetch