Alignment 为什么会有比一个词更大的对齐?

Alignment 为什么会有比一个词更大的对齐?,alignment,cpu,Alignment,Cpu,好的,我知道存储与CPU字大小块对齐的数据可以提高访问速度。但这些块通常是16、32或64位,为什么还有其他的对齐值,比如128bit或256bit?我的意思是,在PC机中没有任何处理器使用如此大的寄存器。我猜这和CPU缓存有关?我也在辅助存储中看到过这种排列(但实际上它们更大——例如10240位)。许多处理器都有128位SIMD寄存器(例如x86 SSE寄存器、ARM Neon寄存器、MIPS SIMD体系结构寄存器);x86 AVX将SSE寄存器扩展到256位,AVX-512将大小再次翻倍

好的,我知道存储与CPU字大小块对齐的数据可以提高访问速度。但这些块通常是16、32或64位,为什么还有其他的对齐值,比如128bit或256bit?我的意思是,在PC机中没有任何处理器使用如此大的寄存器。我猜这和CPU缓存有关?我也在辅助存储中看到过这种排列(但实际上它们更大——例如10240位)。

许多处理器都有128位SIMD寄存器(例如x86 SSE寄存器、ARM Neon寄存器、MIPS SIMD体系结构寄存器);x86 AVX将SSE寄存器扩展到256位,AVX-512将大小再次翻倍

然而,需要更大的路线还有其他原因。正如您所猜测的,缓存行为是使用较大对齐的动机之一。将较大的数据结构与缓存线的大小对齐(x86通常为64字节,现代系统中通常不小于32字节)可以保证对任何成员的访问都会将相同的其他成员带入缓存。这可以通过将经常使用(也称为热)或通常同时使用的成员放在同一个缓存块中来减少缓存容量使用和未命中率

例如,考虑具有32字节高速缓存块的缓存访问的以下结构:

struct {
int64_t hot1; // frequently used member
int64_t hot2; // frequently used member
int64_t hot3; // frequently used member
int64_t hot4; // frequently used member
// end of 32-byte cache block if 32-byte aligned
int64_t a; // always used by func1, func2
int64_t b; // always used by func2
int64_t c; // always used by func1, func3
int64_t d; // always used by func2, func3
// end of 32-byte cache block if 32-byte aligned
int64_t e; // used by func4
int64_t f; // used by func5
int64_t g; // used by func6
int64_t h; // used by func7
}
如果结构是32字节对齐的:

  • 对任何热成员的访问将使所有热成员进入缓存
  • 调用
    func1
    func2
    func3
    a
    b
    c
    d
    带入缓存;如果这些函数在时间上在附近调用,那么数据仍将在缓存中
如果结构是16字节对齐但不是32字节对齐(16字节对齐的概率为50%):

  • hot1
    hot2
    的访问会将位于
    hot1
    之前的16字节无关数据带入缓存,而不会自动将
    hot3
    hot4
    加载到缓存中
  • 访问
    hot3
    hot4
    会将
    a
    b
    带入缓存(可能是不必要的)
  • 调用
    func1
    func2
    更有可能遇到
    a
    b
    的缓存命中,因为它们与
    hot3
    hot4
    位于同一个缓存块中,但未命中
    c
    d
    ,并且将
    e
    f
    带到缓存
  • 调用
    func3
    会降低
    e
    f
    进入缓存的效率,但不会
    a
    b
即使对于小型结构,对齐也可以防止结构(或仅是在时间段中热的或在附近访问的)跨越缓存块边界。例如,将具有16字节热数据的24字节结构与16字节对齐可以保证热数据始终位于同一缓存块中

缓存块对齐还可用于确保两个锁(或由不同线程访问并至少由一个线程写入的其他数据元素)不共享同一个缓存块。这避免了错误的共享问题。(错误共享是指不同线程使用的不相关数据共享一个缓存块。一个线程的写入将从所有其他缓存中删除该缓存块。如果另一个线程写入该块中的不相关数据,它将从第一个线程的缓存中删除该块。对于使用链接加载/存储条件设置锁的ISA,这可能会导致即使没有实际的数据冲突,e-conditional也会失败。)

类似的对齐注意事项适用于虚拟内存页大小(通常为4KB)。通过保证及时访问附近的数据的页数较少,存储虚拟内存地址转换的缓存(转换查找缓冲区[TLB])将不会有太大的容量压力

对齐还可以用于对象缓存中,以减少缓存冲突未命中,这是在项具有相同缓存索引时发生的。(缓存通常仅通过一些最低有效位的选择进行索引。在每个索引处,有限数量的块(称为集合)可用。如果要共享索引的块多于集合中的块(关联性或方式数-),则集合中的一个块必须从缓存中移除以腾出空间。)一个2048字节、完全对齐的内存块可以容纳上述结构的21个副本,并带有一个32字节的填充块(可能用于其他目的)。这保证了来自不同区块的热成员只有33.3%的机会使用相同的缓存索引。(在块中分配,即使没有对齐,也可以保证块中的21个副本都不会共享缓存索引。)

由于简单的按位
可以生成缓冲区的起始地址或缓冲区中的字节数,因此缓冲区中的大对齐也很方便

还可以利用对齐来提供指针压缩(例如,64字节对齐将允许32位指针指向256 GiB而不是4 GiB的地址,但在加载指针时会导致6位左移)。类似地,指向对齐对象的指针的最低有效位可用于存储元数据,在使用指针之前,需要使用
将位归零。

许多处理器都有128位SIMD寄存器(例如x86 SSE寄存器、ARM Neon寄存器、MIPS SIMD体系结构寄存器);x86 AVX将SSE寄存器扩展到256位,AVX-512将大小再次翻倍

然而,需要更大的路线还有其他原因。正如您所猜测的,缓存行为是使用较大对齐的动机之一。将较大的数据结构与缓存线的大小对齐(通常为6
SSE:           16 bytes
AVX:           32 bytes
cache-line:    64 bytes
page:        4096 bytes
SSE: _mm_load_ps() and _mm_store_ps()
AVX: _mm256_load_ps() and _mm256_store_ps()
SSE: _mm_loadu_ps() and _mm_storeu_ps()  
AVX: _mm256_loadu_ps() and _mm256_storeu_ps()