Memory 非时态指令是如何工作的?

Memory 非时态指令是如何工作的?,memory,x86,cpu-architecture,intrinsics,cpu-cache,Memory,X86,Cpu Architecture,Intrinsics,Cpu Cache,我正在读Ulrich Drepper的pdf。在第6部分的开头有一个代码片段: #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_stre

我正在读Ulrich Drepper的pdf。在第6部分的开头有一个代码片段:

#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)
{
__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”,但根据我对流本质的理解,它们绕过了缓存——既没有缓存读取,也没有缓存写入。这段代码将如何访问任何缓存线?第二个粗体部分表示sotheming类似,该函数“避免在写入缓存线之前读取缓存线”。如上所述,我看不到缓存写入的方式和时间。此外,对缓存的任何写入是否需要在缓存写入之前进行?有人能向我澄清一下这个问题吗?

我手指下没有证据来证明我所说的,但我的理解是:通过内存总线传输的唯一单元是缓存线,不管它们是进入缓存还是进入一些特殊的寄存器。所以确实,粘贴的代码填充了缓存线,但它是一个特殊的缓存线,不在缓存中。修改此缓存线的所有字节后,缓存线将直接发送到内存,而不通过缓存。

当您写入内存时,必须首先将写入的缓存线加载到缓存中,以防您只部分写入缓存线

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

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

该注释假设以下行为(但我找不到硬件实际执行此操作的任何参考,需要测量或可靠来源,并且可能因硬件而异): -一旦CPU发现存储缓冲区已满且与缓存线对齐,它将直接将其刷新到内存中,因为非临时写入绕过了主缓存

唯一可行的方法是在刷新存储缓冲区后,将其与实际写入的缓存线合并。这是一个合理的假设

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

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

如果使用非时态写入写入部分缓存线,则可能需要从主内存(或主缓存,如果存在)中提取缓存线,如果我们没有提前使用常规读取或非时态读取(将缓存线放入单独的缓存)读取缓存线,则可能会非常慢

通常,非时态缓存大小约为4-8条缓存线


总而言之,最后一条指令在写操作中起作用,因为它也恰好填满了存储缓冲区。存储缓冲区刷新可以避免读取写入的缓存线,因为硬件知道存储缓冲区是连续的,并且与缓存线对齐。非时态写提示只有在主缓存中还没有写缓存线的情况下才可以避免用写缓存线填充主缓存。

我认为这在一定程度上是一个术语问题:您引用的Ulrich Drepper文章中的一段并不是在谈论缓存数据。它只是对对齐的64B块使用术语“缓存线”

这是正常的,在讨论具有不同缓存线大小的一系列硬件时尤其有用。(早期的x86 CPU,以及最近的PIII,都有32B缓存线,因此使用这个术语可以避免硬编码,从而避免将microch设计决策引入讨论。)


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

您对SSE缓存操作的假设有参考吗?污染是乌尔里希在评论中提到的。我的知识都来自于乌尔里希的论文。在本章前面,他写道:“这些非时态写操作不会读取缓存线,然后对其进行修改;相反,新内容会直接写入内存。”。这是“6.1绕过缓存”第二部分的第二段。我不清楚他想说什么,但如果缓存中碰巧包含地址,MOVNTDQ会更新缓存。@HansPassant:
MOVNTDQ
可以命中缓存,但根据英特尔手册第1卷第10.4.6.2节,暂时数据与非暂时数据的缓存。我猜这个设计决定是为了让驱动程序能够避免NT存储到视频内存或其他东西后出现
clflush
。(一)