7

我正在编写一个程序来解析文件。它由一个主循环组成,该循环逐个字符地解析并处理它们。这是主循环:

char c;
char * ptr;

for( size_t i = 0; i < size ; ++i )
{
    ptr = ( static_cast<char*>(sentenceMap) + i );
    c = *ptr;

    __builtin_prefetch( ptr + i + 1 );

   // some treatment on ptr and c   
}

如您所见,我添加了一条builtin_prefetch指令,希望在循环的下一次迭代中放入缓存。我尝试了不同的值 : ptr+i+1, ptr+i+2ptr+i+10但似乎没有任何改变。

为了测量性能,我使用了 valgrind 的工具 cachegrind,它可以指示缓存未命中的数量。在线c = *ptr时,cachegrind 记录 632,378 DLmr (L3 cache miss) 时__builtin_prefetch未设置。不过奇怪的是,无论我设置的参数如何,这个值都不会改变__builtin_prefetch

对此有何解释?

4

3 回答 3

12

那是因为硬件比你早几年。:)

有一些硬件预取器旨在识别简单的模式并为您进行预取。在这种情况下,您有一个简单的顺序访问模式,这对于硬件预取器来说是微不足道的。

只有当您拥有硬件无法预测的访问模式时,手动预取才会派上用场。

这是一个这样的例子:预取示例?

于 2012-09-19T04:36:54.930 回答
3

首先,调用缓存处理的最小单元cache line,缓存行可以是例如 64 字节长,但永远不会像 1 字节那么小。因此,当您要求预取时,您需要在您当前感兴趣的位置之前提出很多问题。您需要知道缓存线的大小,因此您不应该询问位于同一缓存线中的地址。您还需要不要调用 prefetch 太多次,因为这也可能很快驱逐使用的缓存行,并在执行指令时造成性能损失。

现代架构也有硬件预取器的概念,它可以根据您的访问模式提前为您预取数据。在大多数情况下,这应该是创建与简单预取一样好的数据访问时间。如今,软件预取只能帮助您,前提是您可以找到一个如此明显的地方来预取数据——而不是通过在代码中随机传播。例如在开始处理一条数据之前,但是如果您只是调用 prefetch 并立即访问数据,这对您没有帮助。您需要尽早执行此操作并在访问数据之前完成其他设置工作。

我建议任何对此类主题感兴趣的人阅读The Software Optimization Cookbook。我通常处理 ARM 架构,但我发现这本书非常宝贵。网上也有一些与此问题相关的摘录;见#1#2

于 2012-09-19T06:19:41.920 回答
1

正确的答案是:预取不能改变缓存未命中的数量,它只是迫使它们更早发生:)

于 2014-02-07T16:44:52.207 回答