C++性能优化 —— __builtin_prefetch()数据预读
References
一、什么是 __builtin_prefetch()?
为了降低内存读取的cache-miss延迟,可以通过gcc提供的这个内置函数来预读数据。当知道数据的内存地址即将要被读取(在下一个load & store指令到来之前),在数据被处理之前,就可以在代码中通过指令通知目标,去读取数据并放到缓存中。
Note that the prefetching instruction is a hint, which means that your target processor might, or might not, actually prefetch the data.
二、用法
__builtin_prefetch (const void *addr[, rw[, locality]])
1. addr
(required): 代表内存地址
-
0
(default): prepare the prefetch for a read -
1
: prepare the prefetch for a write to the memory
2. rw
(optional) : 编译时的常量
-
0
: None, the data can be removed from the cache after the access. -
1
: Low, L3 cache, leave the data in the L3 cache level after the access. -
2
: Moderate, L2 cache, leave the data in L2 and L3 cache levels after the access. -
3
(default): High, L1 cache, leave the data in the L1, L2, and L3 cache levels after the access.
三、适用场景
https://zhuanlan.zhihu.com/p/299596490
- prefetch最佳的引用场景是loop循环,因为loop循环中cache miss的概率非常大,我们可以用一个简单的loop来说明如何使用prefetch指令。
- 图a,表示没有prefetch指令的循环体。
- 图b,表示在每次循环体中,都首先预取下一次循环中需要的数据也就是第i+1个数据。
- 图c,假设一个cache line的长度是4个字长,数组中每个元素的大小是一个字长,那么没必要每次都prefetch,因为prefetch本身是有开销的,应该尽量减小它本身带来的开销。所以应该每次prefetch后面4个元素。
- 图d,在图c的基础之上,进一步在循环体之前先预取第0个元素,避免compulsory cache miss,然后在循环末尾再单独处理一下剩下几个元素。
- 大部分的prefetch策略应用场景都是基于数值运算和连续内存访问的程序,这些程序的内存模型更加容易预测,并且有着不是很高的cache利用率,这种程序中使用prefetch容易得到较大的性能提升。
四、缺点
-
prefetch指令本身是有开销的,因为cpu需要存储prefetch指令的目标地址,通常是存在寄存器,这样一来就占用了cpu可用的寄存器空间,如果prefetch指令过多的话还会导致额外的主存访问带来更大的开销.
-
prefetch除了本身的开销之外,还有可能带来cache pollution,如果被prefetch指令换入的cache line有可能在匹配的 load & store指令到来之前,又被换出了cache,这样不仅浪费了一次prefetch,还导致了cache miss。