非时间指令如何工作?

时间:2012-12-31 22:16:23

标签: caching memory x86 intrinsics

我正在阅读Ulrich Drepper的What Every Programmer Should Know About Memory pdf。在第6部分的开头,theres是一个代码片段:

#include <emmintrin.h>
void setbytes(char *p, int c)
{
    __m128i i = _mm_set_epi8(c, c, c, c,
    c, c, c, c,
    c, c, c, c,
    c, c, c, c);
    _mm_stream_si128((__m128i *)&p[0], i);
    _mm_stream_si128((__m128i *)&p[16], i);
    _mm_stream_si128((__m128i *)&p[32], i);
    _mm_stream_si128((__m128i *)&p[48], i);
}

在它下面有这样的评论:

  

假设指针p已正确对齐,则调用此方法   函数将将所寻址的缓存行的所有字节设置为c 。该   写组合逻辑将看到四个生成的movntdq指令   并且只在最后一次发出内存的写命令   指令已执行。总而言之,这个代码序列没有   只有才能避免在写入之前读取缓存行,它也是   避免使用可能不会很快需要的数据来污染缓存。

对我所写的函数的评论是什么让我感到困惑的是它“将把所寻址的缓存行的所有字节都设置为c”但是根据我对流内插的理解他们绕过了缓存 - 既没有缓存读取也没有缓存写作。这段代码如何访问任何缓存行?第二个粗体片段表示相似,即函数“避免在写入之前读取缓存行”。如上所述,我没有看到如何以及何时写入缓存。此外,是否需要在缓存写入之前写入缓存?有人可以向我澄清这个问题吗?

3 个答案:

答案 0 :(得分:3)

当您写入内存时,必须首先将您编写的缓存行加载到缓存中,以防您仅部分写入缓存行。

当您写入内存时,商店会分组到商店缓冲区中。通常,一旦缓冲区已满,它将被刷新到缓存/内存。请注意,存储缓冲区的数量通常很小(~4)。对地址的连续写入将使用相同的存储缓冲区。

具有非时间提示的流式读/写通常用于减少缓存污染(通常使用WC内存)。我们的想法是在CPU上保留一小组缓存行以供这些指令使用。它不是将缓存行加载到主缓存中,而是加载到这个较小的缓存中。

该评论假设以下行为(但我找不到硬件实际执行此操作的任何引用,需要测量或固定源,并且它可能因硬件而异): - 一旦CPU发现存储缓冲区已满并且它与高速缓存行对齐,它将直接将其刷新到内存,因为非临时写入会绕过主高速缓存。

唯一可行的方法是,一旦刷新,就会发生存储缓冲区与写入的实际缓存行的合并。这是一个公平的假设。

请注意,如果写入的缓存行已经在主缓存中,则上述方法也会更新它们。

如果使用常规内存写入而不是非时间写入,则存储缓冲区刷新也会更新主缓存。这种情况完全有可能避免在内存中读取原始缓存行。

如果使用非临时写入写入部分高速缓存行,则可能需要从主存储器(或主高速缓存,如果存在)获取高速缓存行,如果我们还没有读取高速缓存行,则可能非常慢提前读取或非时间读取(将其放入我们的单独缓存中)。

通常,非临时高速缓存大小大约为4-8个高速缓存行。

总而言之,最后一条指令在写入中起作用,因为它也恰好填满了存储缓冲区。存储缓冲区刷新可以避免读取写入的高速缓存行,因为硬件知道存储缓冲区是连续的并且与高速缓存行对齐。非时间写入提示仅用于避免使用我们的写入缓存行IF填充主缓存,并且仅在主缓存中已经存在。

答案 1 :(得分:1)

我认为这部分是一个术语问题:你从Ulrich Drepper的文章中引用的段落并不是在讨论缓存数据。它只是为对齐的64B块使用术语“缓存行”。

这是正常的,在讨论具有不同缓存行大小的一系列硬件时尤其有用。 (早期的x86 CPU,就像最近的PIII一样,有32B缓存行,因此使用这个术语可以避免将微代码设计决策硬编码到讨论中。)

高速缓存行的数据仍然是高速缓存行,即使它在任何高速缓存中当前都不热。

答案 2 :(得分:-2)

我手指上没有引用来证明我在说什么,但我的理解是这样的:通过内存总线传输的唯一单位是缓存行,无论是进入缓存还是某些特殊内容寄存器。实际上,您粘贴的代码填充了一个缓存行,但它是一个不在缓存中的特殊缓存行。一旦修改了该高速缓存行的所有字节,就将高速缓存行直接发送到内存,而不通过高速缓存。