GCC文档讨论了预取读和预取书之间的区别。什么是技术差异?
在CPU级别上,软件预取(与硬件本身触发的触发器相对)是一种提示CPU的方便方法,即即将访问一条行,您是否要提前预取以保存延迟。
如果访问是一个简单的读取,则需要常规的预摘要,该预修的行为与内存的正常负载相似(除了不阻止CPU的情况下,如果它错过了,如果地址不好,则不会出现故障,以及各种其他好处,具体取决于微观架构)。
但是,如果您打算写入该行,并且它也存在于另一个核心中,那么简单的读取操作就不够。这是由于基于MESI的缓存处理协议。核心必须在修改线路之前拥有一条线的所有权,以保持连贯性(如果在多个内核中修改了相同的行,您将无法确保对这些更改的正确订购,甚至可能会丢失其中的一些不允许在普通的WB内存类型上)。取而代之的是,写操作将从获得该行的所有权开始,然后将其从任何其他可以容纳副本的核心/套接字中窃取。只有这样,写入才会发生。读取操作(需求或预取)将以共享状态在其他内核中留下一行,如果许多核心多次读取该行,但是如果您的核心后来写下它,则无济于事。<<<<<<<<<<<<<<<</p>
为了允许对以后将写给的线路的有用预取,大多数CPU公司都支持特殊的预购书写。在X86中,英特尔和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设置,也许其中一些也包括它)。
差异与您期望仅读取该内存或写入记忆有关。在后来的情况下,CPU可能能够以不同的方式优化。请记住,预取只是一个提示,因此GCC可能会忽略它。
引用GCC预取项目页面:
一些数据预取指令在预期将读取的内存和预期将编写的内存之间有区别。当要编写数据时,预取指令可以将块移入缓存中,以便预期的存储将成为缓存。预取书通常以独家或修改的状态将数据带入缓存。