我怎样才能看到软件预取对我的特定程序有帮助?
您可以判断缓存未命中的比例.由于使用了hardware performance counters,因此可以使用perf
或VTune来获取此信息.例如,你可以得到perf list
的列表.该列表取决于目标处理器体系 struct ,但也有一些通用事件.例如,L1-dcache-load-misses
、LLC-load-misses
和LLC-store-misses
.拥有缓存未命中的数量不是很有用,除非您还获得了加载/存储的数量.有L1-dcache-loads
、LLC-loads
或LLC-stores
等通用计数器.另外,对于L2,没有通用计数器(至少在英特尔处理器上),您需要使用特定的硬件计数器(例如,在类似英特尔Skylake的处理器上使用l2_rqsts.miss
).要获得总体统计数据,可以使用perf stat -e an_hardware_counter,another_one your_program
.好的文档可以在here中找到.
当未命中的比例很大时,您应该try 优化代码,但这只是一个提示.事实上,关于应用程序,在应用程序的关键部分/时间,您可能会有很多缓存命中,但很多缓存未命中.因此,缓存未命中可能会丢失.与SIMD相比,标量代码中大量的一级缓存引用尤其如此.一种解决方案是只分析应用程序的特定部分,并利用它的知识朝着好的方向进行调查.性能计数器并不是一个真正的工具来自动搜索程序中的问题,而是a tool to assist you in validating/disproving some hypothesis或give some hints个关于正在发生的事情.它为你提供了破案的证据,但所有的工作都取决于你这个侦探.
如何找到缓存丢失最严重的负载?
一些硬件性能计数器为"precise",这意味着可以找到生成事件的指令.这是very useful,因为您可以判断哪些指令导致了最多的缓存未命中(尽管在实践中并不总是精确).您可以使用perf record
+perf report
来获取信息(有关更多信息,请参阅上一篇教程).
注意there are many reasons that can cause a cache misses and only few cases can be solved by using software prefetching.
如何查看发生未命中的缓存级别来决定使用哪个预取(0,1,2)?
这通常很难在实践中 Select ,并且非常依赖于您的应用程序.理论上,这个数字是hint,用来告诉处理器目标缓存线的局部性级别(例如,取入一级、二级或三级缓存).例如,如果您知道数据应该很快被读取和重用,那么最好将其放在L1中.但是,如果使用了L1,并且您不想用只使用一次(或很少使用)的数据污染L1,那么最好将数据提取到较低的缓存中.在实践中,这有点复杂,因为从一个架构到另一个架构的行为可能不一样...更多信息请参见What are _mm_prefetch()
locality hints?.
例如使用this question.软件预取是用来避免缓存 destruct 问题的,有一些具体的步骤.这是一种病态的情况,硬件预取器不是很有用.
假设我在特定的缓存级别发现了一个因未命中而受到影响的特定负载,那么我应该将预取放在哪里?
这显然是最棘手的部分.您应该足够早地预取缓存线,以便显著减少延迟,否则该指令将无用且无效.实际上,该指令占用了程序中的一些空间,需要进行解码,并使用加载端口来执行其他(更关键的)加载指令.但是,如果太晚了,缓存线可能会被逐出,需要重新加载...
通常的解决方案是编写如下代码:
for (int i = 0; i < n; i++) {
// some code
const size_t magic_distance_guess = 200;
__builtin_prefetch(&data[i+magic_distance_guess]);
double x = a[i];
// some code
}
其中,magic_distance_guess
是一个通常基于基准设定的值(或对目标平台的深刻理解,尽管实践经常表明,即使是高技能的开发人员也无法找到最佳值).
问题是延迟非常依赖于where data are coming from和target platform.In most case, developers cannot really know exactly when to do the prefetching unless they work on a unique given target platform.这使得软件预取很难使用,而且在目标平台发生变化时(必须考虑代码的可维护性和指令的开销)往往是有害的.更不用说内置函数依赖于编译器,预取内部函数依赖于体系 struct ,还有no standard portable way to use software prefetching个.
我是否需要担心展开循环以确保只在缓存线边界上预取,或者如果数据已经在缓存中,它将像nop一样几乎免费?
是的,预取指令不是免费的,因此最好每个缓存线只使用一条指令(因为同一缓存线上的其他预取指令将是无用的).
一行使用多个_内置_预取调用一次预取多个缓存线是否值得?
这非常依赖于目标平台.现代主流x86-64处理器以无序方式并行执行指令,它们有一个相当大的指令分析窗口.他们倾向于尽快执行load,以避免失误,而且他们通常非常适合这样的工作.
在您的示例循环中,我希望在(相对较新的)主流处理器上有the hardware prefetcher should do a very good job and using software prefetching should be slower个.
十年前,当硬件预取器不是很智能时,软件预取很有用,但现在它们往往非常好.此外,指导硬件预取程序通常比使用软件预取指令更好,因为前者的开销更低.这就是为什么software prefetching is discouraged (eg. by Intel and most developers) unless you really know what you are doing.