读或写预取之间的区别

Posted

技术标签:

【中文标题】读或写预取之间的区别【英文标题】:Difference between prefetch for read or write 【发布时间】:2015-05-02 14:20:30 【问题描述】:

gcc docs 讨论了读预取和写预取之间的区别。技术上的区别是什么?

【问题讨论】:

?? “.. 1 表示预取正在准备写入内存地址,默认为零,表示预取正在准备读取。”这是非常具体的。您不确定阅读和写作之间的区别吗? 不,我要改写我的问题,以便更清楚地了解我想知道的内容。 【参考方案1】:

在 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;

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

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

【讨论】:

【参考方案2】:

不同之处在于您是否希望该内存仅很快被读取,或者也被写入。在后一种情况下,CPU 可能能够进行不同的优化。请记住,预取只是一个提示,因此 GCC 可能会忽略它。

引用GCC prefetch project page:

一些数据预取指令会区分预期读取的内存和预期写入的内存。当要写入数据时,预取指令可以将一个块移动到高速缓存中,以便预期的存储将进入高速缓存。写入预取通常会将数据以独占或修改状态带入缓存。

【讨论】:

以上是关于读或写预取之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

浅谈 selectpoll 和 epoll

Unix管道机制

Ingress 和反向代理有啥区别?

APUE---标准I/O库

select简单示例,有注释

「java.util.concurrent并发包」之 ReentrantReadWriteLock