数据预取指的是在实际访问数据之前就预先把该数据读取到缓存中。Intel处理器从P3开始支持软件预取,这是通过在合适的时候预取指令来加载数据完成的。从P4开始,后面的处理器都支持硬解预取,到导致两个连续的缓存缺失的内存地址之间的距离在给定的阈值(128B或者256B)之内时,处理器就会触发数据预取。另外,那些步长为常量(包括向前步长和向后步长)的流式访问也可能触发硬件预取,预取单元可以同时支持多个独立的数据流的预取。基于Netburst体系结构的处理器要求每个流必须属于不同的4KB的页,同时预取也不会超越4KB的边界到另外一个页,因此,硬件预取对于那些小步长切不会超过4KB的数据访问最为有效。基于Core系统结构的处理器允许跨越页的边界,但是仍然对在页内的访问有比较好的性能。硬件预取是自动进行的,而软件预取则可以通过编译器自动或者自己在代码中加入预取指令。Intel编译器的/Qprefetch选项告诉编译器支持软件预取,要使用这个选项,需要和/O3一起使用,但是要注意,由于P4和Core Duo处理器支持硬件预取,如果编译器特别针对这些处理器进行优化,则/Qprefetch不起作用。
除了通过编译器选项来使用软件预取之外,也可以通过prefectch directive来来告诉在访问某些内存时是否进行软件预取;
(1)#pragma noprefetch 指示编译器不进行软件预取
(2)#pragma prefetch指示编译器进行软件预取
(3)#pragma prefetch a,b,指示编译器对于变量a,b的访问进行软件预取。
#pragma noprefetch b
#pragma prefetch a
for(i=0;i  a[i] = b[i]+1;

上面的循环中通过表达式a[j]访问数组,编译器会插入软件预取指令来把a[j+d]加载进缓存中,其中d由编译器分析后决定。

还可以通过SSE Intrinsics来支持软件预取,当然,这必须是在支持SSE扩展的处理器上才发挥作用;
void _mm_prefetch(char const *a,int sel);
它对应着PREFETCH指令,告诉处理器把地址a对应的缓存加载到更高速的缓存中,sel给出了预取操作的类型,如下所示:
PREFETCHINTA    _MM_HINT_NTA  采用非临时预取,减少缓存行的污染
PREFETCH0         _MM_HINT_T0   预取数据到所有缓存
PREFETCH1        _MM_HINT_T1 预取到L2,L3缓存,但是不到L1缓存
PREFETCH2        _MM_HINT_T2   仅预取数据到L3缓存

如果预取的数据仅仅使用一次,一般应该采用非临时预取,如果要进行写操作或者访问该缓存行多次,一般采用预取数据到所有缓存。

for(i=0;i  if((i&2047==0)){
      _mm_prefetch((char *)&arrar[i+2048],_MM_HNT_T0);
}
  Func(&array[i]);
}

上面这个循环,由于步长太大,硬件自动预取无法有效工作。而通过软件预取则可以完成数据预取到缓存行的功能。
09-21 14:08