我想知道这句话的意思,解析。
Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit within a cache line
我实际上无法理解它,我想要一些简单的C代码来解释,非常感谢。
对于 64 字节缓存行(如
alignas(64) char two_lines[128]
),从 0 .. 60 开始的行内任何偏移量的 4 字节访问保证是原子的(如果内存区域可缓存),但 61、62 和 63不是因为访问将被分成两个缓存行。即任何不是缓存行分割的正常大小的访问在可缓存内存上都是原子的。
可以将内存页面或区域标记(使用 PAT 或 MTTR)为可回写式缓存(WB,用于在正常程序中分配内存的所有正常方式)、直写式可缓存 (WT)、不可缓存-写入-组合(WC,通常用于视频 RAM),或完全不可缓存(UC,通常用于 MMIO 的强内存排序规则。)或至少一种其他类型,WP(写保护)也是可缓存的。
这句话仅适用于可缓存内存:WB和WT,而不是UC或WC。底层机制是,对于可缓存内存,加载/存储只是读取或写入CPU缓存中的缓存行。稍后它将作为整个 64 字节单元传输到 DRAM。 (与 AMD 不同,AMD 核心之间的缓存行传输不一定是原子的;这可能是某些边界撕裂的根源。)但对于不可缓存的情况,单个写入可能会通过只能对齐 8 字节的总线发送块或其他东西。
memcpy
之外,C 不提供一种可移植的方式来进行未对齐访问。如果您有 GNU C
typedef uint32_t unaligned_dword __attribute__((aligned(1),may_alias));
,您可以说 *(unaligned_dword*)&two_lines[63]
将访问跨越两个缓存行的 4 字节块,因此不是原子的。
但是,如果编译器使用单个 asm 指令来完成,那么
*(volatile unaligned_dword*)&line[11]
将是原子访问,这在一般 C 中也不能保证(但如果 GCC 和 clang 可以使用 volatile
的话,它们会这样做)。如果您确实希望编译器使用原子访问来制作汇编,则需要 _Atomic
。 Intel 手册记录的是 asm 保证,而不是 C。
此规则通常与 C 语言编程无关,因为 2、4 和 8 字节类型在标准 x86-64 ABI 中具有
alignof(T) = sizeof(T)
,因此在没有 GNU C 属性等扩展的情况下对它们进行未对齐访问是无效的。在 i386 System V ABI 中,alignof(double)
只是 4
,因此您可以拥有未对齐的 double 或 int64_t,但无法声明可以在缓存行内未对齐但不能跨缓存行拆分的对象。 (除了可能是alignas(64) struct
的一部分。)
相关:为什么在 x86 上自然对齐变量上的整数赋值是原子的? 讨论了这句话以及它如何比 AMD 的保证更强。 (只有 8 字节 qword 内的未对齐访问才能保证原子性,即使在可缓存内存上也是如此。)