【发布时间】:2012-12-15 21:37:21
【问题描述】:
我正在阅读 Ulrich Drepper 的 What Every Programmer Should Know About Memory 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。这 写组合逻辑将看到四个生成的 movntdq 指令 并且仅在最后一次发出内存写入命令 指令已执行。总而言之,这个代码序列不是 只有避免在写入之前读取缓存行,它还 避免用可能很快不需要的数据污染缓存。
让我感到困扰的是,在对函数的注释中写道,它“会将寻址缓存行的所有字节设置为 c”,但根据我对流内部的理解,它们绕过缓存 - 既没有缓存读取,也没有缓存写入。此代码将如何访问任何缓存行?第二个粗体片段表示类似,该函数“避免在写入之前读取缓存行”。如上所述,我看不到任何缓存的写入方式和时间。此外,是否需要在缓存写入之前对缓存进行任何写入?有人可以向我澄清这个问题吗?
【问题讨论】:
-
您对 SSE 缓存操作的假设是否有参考? Intel documentation 指定了 Ulrich 在评论中引用的污染。
-
我的知识全部来自 Ulrich 的论文。在本章的前面,他写道:“这些非临时写操作不会读取缓存行然后对其进行修改;而是将新内容直接写入内存。”。它来自“6.1 绕过缓存”部分的第二段
-
我不清楚他想说什么,但 MOVNTDQ 确实更新缓存,如果它恰好包含地址。
-
@HansPassant:
movntdq可以命中缓存,但 it evicts the line from cache if it was present,根据英特尔手册 vol1 ch 10.4.6.2 缓存临时数据与非临时数据。我猜这个设计决定是为了让驱动程序可以在 NT 存储到视频内存或其他东西之后避免clflush。 (IIRC,文档说这种保证驱逐不会发生在早期的 CPU 上以支持该指令。) -
_mm_set1_epi8(c)比输入c16 次更容易广播一个字节。
标签: memory x86 cpu-architecture intrinsics cpu-cache