好的我知道存储与CPU字大小的块对齐的数据会增加访问它的速度。但是这些块通常是16位,32位或64位,为什么还有其他对数值,如128位或256位?我的意思是没有任何处理器在PC中使用如此大的寄存器。我认为这与CPU缓存有关吗?我也在二级存储中看到了这样的对齐(但实际上它们更大 - 例如10240bit。)
答案 0 :(得分:1)
许多处理器做具有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字节对齐(50%的几率与16字节对齐):
hot1
或hot2
会在hot1
之前带来16字节的无关数据,而不会自动将hot3
和hot4
加载到缓存< / LI>
hot3
或hot4
会将a
和b
带入缓存(可能不必要)func1
或func2
的调用更有可能遇到a
和b
的缓存命中,因为它们与hot3
位于同一缓存块中}和hot4
但是c
和d
错过了,并且不太有用地将e
和f
带入缓存。func3
的调用不会有效地将e
和f
带入缓存,但不会a
和b
即使对于小型结构,对齐也可以防止结构(或仅仅是热的或访问时间附近的部分)跨越高速缓存块边界。例如,将24字节结构与16字节热数据对齐到16字节可以保证热数据始终位于同一高速缓存块中。
高速缓存块对齐也可用于保证两个锁(或由不同线程访问并由至少一个写入的其他数据元素)不共享相同的高速缓存块。这可以避免错误的共享问题。 (虚假共享是指不同线程使用的不相关数据共享一个缓存块。一个线程写入将从所有其他缓存中删除该缓存块。如果另一个线程写入该块中的不相关数据,它将从第一个线程中删除该块对于使用链接加载/存储条件来设置锁定的ISA,这可能导致存储条件失败,即使没有实际的数据冲突。)
类似的对齐注意事项适用于虚拟内存页面大小(通常为4KiB)。通过保证及时访问附近的数据的页数较少,存储虚拟内存地址(转换后备缓冲区[TLB])的转换的缓存不会有那么大的容量压力。
也可以在对象缓存中使用对齐来减少缓存冲突丢失,这在项目具有相同的缓存索引时会发生。 (高速缓存通常仅通过选择一些最低有效位来索引。在每个索引处,有限数量的块(称为集合)可用。如果更多块想要共享索引而不是集合中的块 - 关联性或方式的数量 - 然后必须从缓存中移除集合中的一个块以腾出空间。)一个2048字节,完全对齐的内存块可以容纳21个上述结构的副本,带有32字节的填充块(可能用于其他目的)。这可以保证来自不同块的热门成员只有33.3%的机会使用相同的缓存索引。 (分配在一个块中,即使没有对齐,也可以保证块中的21个副本都不会共享缓存索引。)
大对齐在缓冲区中也很方便,因为简单的按位and
可以产生缓冲区的起始地址或缓冲区中的字节数。
也可以利用对齐来提供指针压缩(例如,64字节对齐将允许32位指针在加载指针时以6位左移为代价来寻址256 GiB而不是4 GiB)。类似地,指向对齐对象的指针的最低有效位可用于存储元数据,在使用指针之前需要and
将这些位置零。
答案 1 :(得分:1)
以下是我使用过的路线:
SSE: 16 bytes
AVX: 32 bytes
cache-line: 64 bytes
page: 4096 bytes
SSE和AVX都提供加载和存储指令,这些指令要求SSE为16字节,AVX为32字节。 E.g。
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字节)。