为什么某些类型(例如Float80)的内存对齐大于字大小?

Bin*_*ian 6 memory-alignment swift

为了使它具体,我只想知道为什么在我的64位mac上,Swift编译器说某些类型的对齐方式Float80是16. 要检查类型的内存对齐要求,我使用该alignof函数.

sizeof(Float80) // ~> 16 bytes, it only needs 10 bytes, but because of hardware design decisions it has to be a power of 2
strideof(Float80) // ~> 16 bytes, clear because it is exact on a power of 2, struct types with Float80 in it, can be bigger
alignof(Float80) // ~> 16 bytes, why not 8 bytes, like String ?
Run Code Online (Sandbox Code Playgroud)

我理解类型的内存对齐小于或等于单词的大小是有益的.

sizeof(String) // ~> 24 bytes, clear because 24 is multiple of 8
strideof(String) // ~> 24 bytes, clear because 24 is multiple of 8
alignof(String) // ~> 8 bytes, clear because something greater or equal to 8 bytes should align to 8 bytes
Run Code Online (Sandbox Code Playgroud)

许多具有更大内存大小的类型String(大小为24)确实具有8字节的内存对齐要求.我希望这是我正在使用的CPU/RAM总线的大小,因为我有一个64位的mac和os. 我检查了类型的大小,没有使用sizeof函数的最后一个填充,并且使用函数添加填充到末尾strideof(strideof在结构数组中更有用,然后Swift在末尾添加字节以达到对齐的下一个倍数需求.)

我知道填充对于小于或等于8字节大小的类型是必要的.

但我不明白为什么在我的64位mac上有一个大于8字节的内存对齐要求是有利的.

Float80的值需要80位,即10个字节,有6个填充字节.

这是一张图片,使其更加清晰,我的意思.Float80允许绿色位置,红色位置不允许. 该图中的内存为8字节块.

Swift中的字节和字Float80内存对齐

Mar*_*n R 7

所有"原始数据类型"(该术语可能是错误的,我的意思是处理器使用的数据类型)具有"自然边界",并且编译器将相应地将它们对齐在内存中.对齐取决于处理器(例如x86或ARM)和编程环境(例如32位与64位).有些处理器允许未对齐的数据(可能以较低的速度),有些则不允许.

对于64位Intel架构,迁移到64位Intel®架构时,数据对齐中列出了这些要求 :

但是,64位环境对数据项提出了更严格的要求.未对齐的对象会导致程序异常.
[...]

  • 在任何地址对齐8位数据
  • 将16位数据对齐以包含在对齐的四字节字中
  • 对齐32位数据,使其基址为四的倍数
  • 对齐64位数据,使其基址为8的倍数
  • 对齐80位数据,使其基址为十六的倍数
  • 对齐128位数据,使其基址为16的倍数

因此,对齐不一定等于"字大小",它可以更少或更多.Float80对应于x86处理器的"扩展精度"浮点类型,其对齐需要为16个字节.

像C这样的复合类型struct在内存中布局,使得每个成员都在其自然边界上(如果需要,填充插入其间).结构本身的对齐是每个成员的最大对齐.

Swift的内存布局Struct没有正式记录(据我所知)但它可能与C类似struct.这是一个简单的例子:

struct AStruct {
    var a = Int32(0)
    var b = Int8(0)
    var c = Int16(0)
    var d = Int8(0)
}
println(sizeof(AStruct))     // 9
println(alignof(AStruct))    // 4
println(strideof(AStruct))   // 12
Run Code Online (Sandbox Code Playgroud)

内存布局(可能)是(*=填充):

aaaab*ccd
Run Code Online (Sandbox Code Playgroud)

这里的对齐是4,因为这是所需的对齐方式Int32.结构占用9个字节,但"步幅"为12:这保证了在结构数组中所有元素都满足相同的对齐.

(注意,Swift strideOf()对应于C sizeof()函数,这在https://devforums.apple.com/message/1086107#1086107中有解释.)

Swift字符串的声明显示为

struct String {
    init()
}
Run Code Online (Sandbox Code Playgroud)

但我们凡人都看不到实际的成员.在调试器中它看起来像这样:

在此输入图像描述

表示其成员是指针,无符号字和另一个指针.所有这些类型在64位上具有8字节的大小和对齐.这将解释的大小(24字节)和对齐(8字节)struct Swift.

  • @ViktorLexington:然后我误解了你的问题.我想简短的回答是"因为英特尔就是这样设计的".如果您的问题是关于处理器设计,那么我无能为力. - 注意`String`是一个`Struct`,因此不同于"primitive"类型. (5认同)

Bin*_*ian 4

借助 Martin R 的链接并暗示这是处理器设计决策。我找到了原因。

缓存行。

缓存线对于处理器来说是一个非常小的内存,在我的 Intel Mac 64 位上它是 128 位(16 字节)。

从问题的图片中可以看出,我知道虚线和粗线之间存在差异。粗线位于处理器的高速缓存线之间。如果您可以通过增加一点内存成本来做得更好,那么您不想加载 2 个缓存行。因此,如果处理器仅允许,则大小为 8 字节(或更大)的类型会在缓存行的开头对齐(每 16 的倍数)。对于与缓存行一样大的类型,不会有两次缓存行读取(在我的情况下是字大小的两倍,16 字节)。正如您在图片中看到的,只有红色块穿过粗线(因此每个设计不允许它们)。

请参阅随附的链接以获取更多信息。

缓存效果