1

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

4

2 回答 2

2

以下是我使用的对齐方式:

SSE:           16 bytes
AVX:           32 bytes
cache-line:    64 bytes
page:        4096 bytes

SSE 和 AVX 都提供加载和存储指令,要求 SSE 对齐到 16 字节或 AVX 对齐到 32 字节。例如

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()

在 Nahelem 之前,未对齐的加载/存储即使在对齐的内存上也比需要对齐的指令具有更大的延迟/吞吐量。但是,由于 Nahelem,它们在对齐的内存上具有相同的延迟/吞吐量,这意味着没有理由再使用需要对齐的加载/存储指令。这并不意味着对齐的内存不再重要。

如果 16 或 32 字节跨越高速缓存行,并且这 16 或 32 字节被加载到 SSE/AVX 寄存器中,这可能会导致停顿,因此它也有助于与高速缓存行对齐。在实践中,我通常对齐到 64 个字节。

在多个处理器之间共享内存的多插槽系统上,比访问每个处理器的主内存要慢。出于这个原因,它可以帮助确保内存不会在通常但不一定是 4096 字节的虚拟页面之间分割。

于 2014-04-16T09:06:11.533 回答
2

许多处理器确实有 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, orfunc3会将a, b, c, andd带入缓存;如果这些函数在附近被及时调用,那么数据仍然会在缓存中

如果结构是 16 字节对齐但不是32 字节对齐(16 字节对齐的可能性为 50%):

  • 访问hot1hot2将带来位于之前的 16 字节不相关数据hot1并且不会自动加载hot3hot4进入缓存
  • 访问hot3hot4将带入ab带入缓存(可能不必要)
  • 调用func1orfunc2更有可能遇到 和 的缓存命中ab因为这些将与 和 位于同一缓存块中hot3hot4但有一个未命中 ,c并且d不太有用地将ef带入缓存。
  • 调用func3将不太有用地将eandf带入缓存,但不会aandb

即使对于一个小结构,对齐也可以防止结构(或只是热的或访问时间附近的部分)跨越缓存块边界。例如,将具有 16 字节热数据的 24 字节结构对齐到 16 字节可以保证热数据将始终在同一个缓存块中。

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

类似的对齐考虑适用于虚拟内存页面大小(通常为 4KiB)。通过保证及时访问的数据在较少的页数中,存储虚拟内存地址转换的缓存(转换后备缓冲区[TLB])不会有太大的容量压力。

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

大对齐在缓冲区中也很方便,因为简单的按位and可以产生缓冲区的起始地址或缓冲区中的字节数。

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

于 2014-04-15T22:51:14.117 回答