读取或写入的预取之间的区别

use*_*011 4 c gcc prefetch

GCC文档讲读和预取写预取之间的差异.有什么技术差异?

Lee*_*eor 7

在CPU级别上,软件预取(与硬件本身触发的预取相反)是向CPU提示即将访问线路的一种便捷方式,并且您希望预先预取它以节省延迟.

如果访问将是一个简单的读取,你会想要一个常规的预取,它的行为类似于来自内存的正常加载(除了没有阻塞CPU以防它错过,如果地址坏了没有错误,并且所有种类其他好处,取决于微观架构).

但是,如果您打算写入该行,并且它也存在于另一个核心中,则简单的读取操作是不够的.这是由于基于MESI的缓存处理协议.核心在修改之前必须拥有一条线,这样它才能保持一致性(如果同一条线在多个核心中被修改,你将无法确保这些变化的正确排序,甚至可能会失去其中的一些,正常的WB内存类型不允许).相反,写操作将从获取该行的所有权开始,并将其从可能存在副本的任何其他核心/套接字中窥探出来.只有这样才能发生写入.读取操作(需求或预取)会使其他内核中的行处于共享状态,如果许多内核多次读取该行,这很好,但如果您的内核稍后写入它则无法帮助您.

为了允许对稍后写入的行进行有用的预取,大多数CPU公司都支持用于写入的特殊预取.在x86中,Intel和AMD都支持prefetchW指令,该指令应该具有写入的效果(即 - 获取线路的唯一所有权,并且如果有则使其他任何副本无效).请注意,并非所有CPU都支持(即使在同一系列中,并非所有代都支持它),并非所有编译器版本都支持它.

这是一个例子(使用gcc 4.8.2) - 请注意,您需要在此处显式启用它 -

#include <emmintrin.h>

int main() {
    long long int a[100];
    __builtin_prefetch (&a[0], 0, 0);
    __builtin_prefetch (&a[16], 0, 1);
    __builtin_prefetch (&a[32], 0, 2);
    __builtin_prefetch (&a[48], 0, 3);
    __builtin_prefetch (&a[64], 1, 0);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

汇编gcc -O3 -mprfchw prefetchw.c -c,:

0000000000000000 <main>:
   0:   48 81 ec b0 02 00 00    sub    $0x2b0,%rsp
   7:   48 8d 44 24 88          lea    -0x78(%rsp),%rax
   c:   0f 18 00                prefetchnta (%rax)
   f:   0f 18 98 80 00 00 00    prefetcht2 0x80(%rax)
  16:   0f 18 90 00 01 00 00    prefetcht1 0x100(%rax)
  1d:   0f 18 88 80 01 00 00    prefetcht0 0x180(%rax)
  24:   0f 0d 88 00 02 00 00    prefetchw 0x200(%rax)
  2b:   31 c0                   xor    %eax,%eax
  2d:   48 81 c4 b0 02 00 00    add    $0x2b0,%rsp
  34:   c3                      retq
Run Code Online (Sandbox Code Playgroud)

如果您使用第二个参数,您会注意到prefetchW的提示级别被忽略,因为它不支持时间级别提示.顺便说一句,如果你删除-mprfchw标志,gcc会将其转换为正常的读取预取(我没有尝试过不同的-march/mattr设置,也许其中一些也包括它).