现代处理器和内存旨在尽可能优化内存访问。当前访问存储器的一种方法是不是按字节寻址而是通过更大块的地址来寻址,例如由8个字节块组成。这样,您不需要地址的3个低位。为了访问块中的某个字节,进程需要将块移到对齐的地址,然后移位并屏蔽该字节。因此,它变慢了。
当结构中的字段未对齐时,可能会减慢对其的访问。因此,最好将它们对齐。
但是资格要求是基于基础平台的。对于支持字访问(32位)的系统,4字节对齐是可以的,否则可以使用8字节或其他方式。编译器(和libc)知道要求。
我正在尝试重新实现malloc,我需要了解对齐的目的。据我了解,如果内存对齐,则代码将更快地执行,因为处理器不必采取额外的步骤来恢复被削减的内存位。我想我知道64位处理器读取64位乘64位内存。现在,让我们想象一下,我有一个结构按顺序排列(没有填充):一个char,一个short,一个char和一个int。为什么短路会错位?我们将所有数据存储在块中!为什么它必须位于2的倍数的地址上?对于整数和其他类型也有相同的问题吗?
我还有第二个问题:利用前面提到的结构,处理器如何在读取其64位时知道前8位对应于char,然后接下来的16位对应于short等...?
这些效果甚至可以包括正确性,而不仅仅是性能:如果您的short
对象不满足alignof(short)
,则C不确定行为(UB)可能导致段错误或其他不良行为。 (在默认情况下,加载/存储指令要求对齐的ISA上可能会发生故障,例如SPARC和MIPS64r6之前的MIPS)
或者如果_Atomic int
没有alignof(_Atomic int)
,则会破坏原子操作。>>
(通常为alignof(T) = sizeof(T)
,最大为某些大小,在任何给定的ABI中通常为寄存器宽度或更宽)。
[malloc
应该以alignof(max_align_t)
]返回内存,因为您没有任何类型信息。或者,对于小于sizeof(max_align_t)
的分配,如果需要,您can
([alignas (8) int32_t foo
之类的过度对齐的东西需要使用C11 aligned_alloc
之类的特殊分配器。如果要实现自己的分配器库,则可能要支持aligned_realloc和aligned_calloc,以填补ISO C所需要解决的空白没有明显的原因,并确保您不对分配大小不是比对的倍数执行[de]不执行aligned_alloc
的Braindead ISO C ++ 17要求。没有人希望分配器拒绝在16个字节的边界上分配101个浮点数。aligned_alloc function requirements和How to solve the 32-byte-alignment issue for AVX load/store operations?)
我想我知道64位处理器可以通过64位内存读取64位
不。数据总线宽度和突发大小,以及加载/存储执行单元的最大宽度或实际使用的宽度不必与整数寄存器的宽度相同,但是CPU可以定义其位数。 (并且在现代高性能CPU中通常没有,例如32位P5 Pentium具有64位总线;现代32位ARM具有执行原子64位访问的加载/存储对指令。)
处理器将整个缓存行从DRAM / L3 / L2缓存读取到L1d缓存;在现代x86上为64字节;在其他一些系统上为32个字节。
并且当读取单个对象或数组元素时,它们从L1d缓存中读取元素宽度。例如uint16_t
数组可能仅受益于2字节加载/存储的2字节边界对齐。
或者如果编译器使用SIMD对循环进行矢量化处理,则可以一次读取uint16_t
数组16或32 bytes
缓存行拆分,尤其是页面拆分是现代x86因未对齐而减慢的地方;在高速缓存行中未对齐的晶体管通常不是因为它们花费晶体管来进行快速未对齐的加载/存储。其他一些ISA在任何未对齐情况下都变慢,甚至出现故障,即使在缓存行内也是如此。解决方案是相同的:给类型自然对齐:alignof(T)= sizeof(T)。
在您的结构示例中,即使short
未对齐,现代x86 CPU也不会受到任何影响。任何普通ABI中的alignof(int) = 4
,因此整个结构都具有alignof(struct) = 4
,因此char;short;char
块从4字节边界开始。因此,short
包含在单个4字节双字中,没有越过任何较宽的边界。 AMD和Intel都以充分的效率处理此问题。 (并且x86 ISA保证对它的访问在与P5 Pentium或更高版本兼容的CPU上是原子的,甚至是未缓存的:Why is integer assignment on a naturally aligned variable atomic on x86?)
某些非x86 CPU将因未对齐的short受到惩罚,或必须使用其他指令。 (由于您知道相对于对齐的32位块的对齐方式,因此对于负载,您可能会进行32位的加载和移位。)
所以是的,访问包含short
的单个单词没有问题,但是问题是负载端口硬件将short
提取并零扩展(或符号扩展)到完整寄存器中。] >这是x86花费晶体管使速度更快的地方。
将存储重新分配到缓存中也是不容易的。例如,一级缓存可能具有32位或64位块中的ECC(针对位翻转的纠错)。因此,由于这个原因,只写一个字的一部分是一个问题,而且要将其从要访问的缓存中移到该字内的任意字节边界。 (在商店缓冲区中对附近商店进行联合销售可以避免RMW周期来更新单词的一部分)。请注意,我之所以说“单词”,是因为我说的是硬件更注重单词,而不是像现代x86那样围绕未对齐的加载/存储进行设计。 请参见Are there any modern CPUs where a cached byte store is actually slower than a word store?(仅存储一个字节比未对齐的short
稍微简单一些)
当然,short
的未对齐原因很简单,即alignof(short) = 2
违反了此ABI规则(假设ABI确实具有该规则)。因此,如果将指向它的指针传递给其他函数,则可能会遇到麻烦。尤其是在负载错位错误的CPU上,而不是由硬件处理这种情况,这种情况在运行时被证明是错位的。然后,您可能会遇到类似Why does unaligned access to mmap'ed memory sometimes segfault on AMD64?的情况,其中GCC自动矢量化通过对2字节元素进行标量的倍数处理,有望达到16字节边界,因此违反ABI会导致x86出现段错误(通常容忍未对齐) 。)
有关内存访问的完整详细信息,从DRAM RAS / CAS延迟到高速缓存带宽和对齐方式,请参阅What Every Programmer Should Know About Memory?仍然很重要/适用)>
也Purpose of memory alignment有一个很好的答案。 SO的memory-alignment标签还有很多其他好的答案。
有关(某种程度上)现代英特尔加载/存储执行单元的更多详细信息,请参见:https://electronics.stackexchange.com/questions/329789/how-can-cache-be-that-fast/329955#329955
处理器如何在读取其64位时知道前8位对应于一个char,然后接下来的16位对应于short等...?
除了它正在运行的指令以这种方式处理数据之外,没有。 在asm /机器代码中,一切都只是字节。
现代处理器和内存旨在尽可能优化内存访问。当前访问存储器的一种方法是不是按字节寻址而是通过更大块的地址来寻址,例如由8个字节块组成。这样,您不需要地址的3个低位。为了访问块中的某个字节,进程需要将块移到对齐的地址,然后移位并屏蔽该字节。因此,它变慢了。
当结构中的字段未对齐时,可能会减慢对其的访问。因此,最好将它们对齐。
但是资格要求是基于基础平台的。对于支持字访问(32位)的系统,4字节对齐是可以的,否则可以使用8字节或其他方式。编译器(和libc)知道要求。
因此,在您的示例char,short,char中,如果未填充,则short将以奇数字节位置开始。要访问它,系统可能需要读取该结构的64位字,然后将其右移1个字节,然后屏蔽2个字节,以便为您提供此字节。
现代处理器和内存旨在尽可能优化内存访问。当前访问存储器的一种方法是不是按字节寻址而是通过更大块的地址来寻址,例如由8个字节块组成。这样,您不需要地址的3个低位。为了访问块中的某个字节,进程需要将块移到对齐的地址,然后移位并屏蔽该字节。因此,它变慢了。
当结构中的字段未对齐时,可能会减慢对其的访问。因此,最好将它们对齐。
但是资格要求是基于基础平台的。对于支持字访问(32位)的系统,4字节对齐是可以的,否则可以使用8字节或其他方式。编译器(和libc)知道要求。