【问题标题】:Why do some types (e.g. Float80) have a memory alignment bigger than word size?为什么某些类型(例如 Float80)的内存对齐大于字长?
【发布时间】:2015-03-06 11:56:39
【问题描述】:

具体来说,我只想知道为什么在我的 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 ?

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

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

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

我了解小于或等于 8 字节大小的类型需要填充。

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

Float80 的值需要 80 位,即 10 个字节,其中 6 个填充字节。

我的意思是,为了更清楚,这是一张图片。 Float80 允许绿色位置,红色位置不允许。 在这张图中,内存是 8 个字节的块。

【问题讨论】:

  • 您的图片似乎具有误导性。如果一行代表 8 个字节,则 Float80(包括填充)只能占用两行,而不是 16 行。如果一行代表 1 个字节,则第 3 列中的 Float80 不在对齐地址上。
  • @MartinR 你说的很对,换了图片
  • 24 是 2 的精确幂??
  • @SlippD.Thompson 你是对的,那是一个错误。感谢您的提示。我纠正了它。 8 是字长,24 是 8 的倍数。

标签: swift memory-alignment


【解决方案1】:

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

对于 64 位 Intel 架构,要求列于 Data Alignment when Migrating to 64-Bit Intel® Architecture:

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

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

所以对齐不一定等于“字长”,可以 更少或更多。 Float80 对应于“扩展精度” x86 处理器的浮点类型,其对齐方式为 要求为 16 个字节。

像 C struct 这样的复合类型在内存中布局,使得 每个 member 都在其自然边界上(并且插入了填充 如有必要,介于两者之间)。结构对齐 本身就是每个成员的最大对齐。

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

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

aaaab*ccd

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

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

Swift 字符串的声明显示为

struct String {
    init()
}

但实际的成员对我们这些凡人来说是不可见的。 在调试器中它看起来像这样:

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

【讨论】:

  • 你没有回答我的问题。我知道所需的对齐方式,问题是为什么在 64 位机器上我需要大于 8 字节的对齐方式。我没有看到优势。具体为什么 Float80(10+6 字节大小)有 16 字节而不是 8 字节的对齐要求?一个字符串(24 个字节)需要 8 个字节。当 Float80 跟随时,大小为 1 字节的 Bool 将为结构添加 15 字节的大小,为什么 7 字节不够。由于阅读时的性能,最小是字长(示例在维基百科等),但永远不会大于字长。
  • @ViktorLexington:那我误解了你的问题。我想简短的回答是“因为英特尔是这样设计的”。如果您的问题是关于处理器设计的,那么我无能为力。 – 请注意,StringStruct,因此不同于“原始”类型。
  • 然后将字符串作为具有 Chars 和 Ints 的结构,每个人都有 8 个字节的对齐,那么 String 结构也具有 8 的对齐是有道理的。很高兴知道。
  • @ViktorLexington:我添加了一些关于字符串类型的信息。
  • 感谢您提示我正确的方向,找到了答案 :-)
【解决方案2】:

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

缓存行。

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

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

有关详细信息,请参阅随附的链接。

Cache effects

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 2017-06-27
    • 1970-01-01
    • 1970-01-01
    • 2021-02-28
    • 1970-01-01
    • 2012-01-15
    相关资源
    最近更新 更多