The prefetch instruction
似乎预取使用的一般逻辑是可以添加预取,前提是代码在预取指令完成其操作之前一直忙于处理。但是,如果使用了太多的预取指令,则可能会影响系统的性能。我发现我们首先需要有没有预取指令的工作代码。稍后,我们需要在不同的代码位置组合各种预取指令,并进行分析,以确定由于预取而实际可以改进的代码位置。有没有更好的方法来确定使用预取指令的确切位置?
在大多数情况下,预取指令几乎没有好处,甚至在某些情况下可能适得其反。大多数现代的CPU都有一个自动预取机制,它可以很好地工作,添加软件预取提示几乎不会实现,甚至不会干扰自动预取,实际上可以降低性能。
在一些罕见的情况下,例如,当您流式处理的数据块很大,而实际处理很少,您可能会设法用软件启动的预取隐藏一些延迟,但很难正确地进行预取-您需要在使用数据之前启动几百个周期的预取-太迟了而且,您仍然会错过缓存,太早执行,在准备使用它之前,您的数据可能会从缓存中被逐出。这通常会将预取放在代码的一些无关部分,这对模块化和软件维护不利。更糟糕的是,如果您的体系结构发生了变化(新的CPU、不同的时钟速度等),导致DRAM访问延迟增加或减少,您可能需要将预取指令移动到代码的另一部分,以保持它们的有效性。
不管怎样,如果您觉得您真的必须使用预取,我建议ifdefs绕过任何预取指令,这样您就可以编译有预取和无预取的代码,并查看它是否真的有助于(或阻碍)性能,例如。
1 2 3 | #ifdef USE_PREFETCH // prefetch instruction(s) #endif |
不过,总的来说,我建议把软件预取放在次要位置,作为最后的手段,在您完成了所有更有成效和明显的工作之后进行微观优化。
甚至要考虑预取代码的性能也必须是一个问题。
1:使用代码分析器。尝试在没有分析器的情况下使用预取是浪费时间的。
2:每当你在一个非常慢的关键地方发现一条指令,你就有了一个预取的候选人。通常实际的问题是在慢内存访问之前的一行内存访问,而不是分析器所指示的慢内存访问。找出导致问题的内存访问(并不总是容易的)并预取它。
3再次运行分析器,看看它是否有什么不同。如果它没有取出。有时,我会通过这种方式将循环加速300%。如果有一个循环以非顺序的方式访问内存,那么它通常是最有效的。
我完全不同意它在现代CPU上不太有用,我发现完全相反,尽管在旧的CPU上预取大约100条指令是最佳的,但今天我把这个数字放在500左右。
当然,您必须体验一点,但不是说您需要在需要数据之前获取一些特定的循环(100-300)。二级缓存非常大,因此预取的数据可以保留一段时间。
这种预取在一个循环(当然是几个受约束的循环)前面非常有效,特别是如果它是内部循环,并且循环每秒钟启动一千次以上。
另外,对于我们的低速ll实现或树实现,预取可以获得一个可测量的优势,因为CPU不知道Jet很快就需要数据。
但是请记住,预取指令会占用一些解码器/队列带宽,因此过度使用它们会因此而损害性能。