为什么某些类型(例如 Float80)的内存对齐大于字长?
Why do some types (e.g. Float80) have a memory alignment bigger than word size?
具体来说,我只想知道为什么在我的 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 和 os。
我使用 sizeof
函数检查没有最后填充的类型的大小,并使用 strideof
函数在末尾添加填充(strideof 在结构数组中更有用, Swift然后在末尾添加字节以达到对齐要求的下一个倍数。)
我知道对于小于或等于 8 字节大小的类型,填充是必要的。
但我不明白为什么在我的 64 位上内存对齐要求大于 8 个字节是有利的mac。
Float80 的值需要 80 位,即 10 个字节,6 个填充字节。
这是一张图片,可以更清楚地说明我的意思。
绿色 pos 允许 Float80,红色 pos 不允许。
这张图片中的内存是8字节块。
全部"primitive data types"(这个词可能有误,我的意思是
处理器使用的数据类型)有一个 "natural boundary",
并且编译器将相应地在内存中对齐它们。对齐方式
取决于处理器(例如 x86 或 ARM)和编程环境(例如 32 位与 64 位)。
一些处理器允许未对齐的数据(可能以较低的速度),
有些不允许。
对于 64 位 Intel 架构,要求列在
Data Alignment when Migrating to 64-Bit Intel® Architecture:
The 64-bit environment, however, imposes more-stringent requirements on data items. Misaligned objects cause program exceptions.
[...]
- Align 8-bit data at any address
- Align 16-bit data to be contained within an aligned four-byte word
- Align 32-bit data so that its base address is a multiple of four
- Align 64-bit data so that its base address is a multiple of eight
- Align 80-bit data so that its base address is a multiple of sixteen
- Align 128-bit data so that its base address is a multiple of sixteen
所以对齐不一定等于"word size",可以
更少或更多。 Float80
对应 "Extended Precision"
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 个字节,但 "stride" 为 12:
这保证在结构数组中所有元素都满足
相同的路线。
(注意SwiftstrideOf()
对应C的sizeof()
函数,这个在https://devforums.apple.com/message/1086107#1086107中有解释。)
Swift 字符串的声明显示为
struct String {
init()
}
但是我们凡人是看不到实际成员的。
在调试器中它看起来像这样:
表示它的成员是一个指针,
一个无符号字和另一个指针。所有这些类型都有大小和
在 64 位上对齐 8 个字节。这将解释大小(24 字节)
struct Swift
.
的对齐方式(8 字节)
在 Martin R 的帮助下 link 以及它是处理器设计决策的提示。我找到了原因。
缓存行。
缓存线是处理器的非常小的内存,在我的 Intel Mac 64 位上它是 128 位(16 字节)。
从问题的图片中可以看出,我知道虚线和粗线之间存在差异。粗线位于处理器的高速缓存行之间。如果你可以用更多的内存成本做得更好,你不想加载 2 个缓存行。因此,如果处理器只允许,大小为 8 字节(或更大)的类型将在缓存行的开头对齐(每 16 的倍数)。对于与缓存行一样大的类型(在我的情况下是字大小的两倍,16 字节),不会有两次缓存行读取。正如您在图片中看到的,只有红色块穿过粗线(因此每个设计都不允许使用它们)。
有关详细信息,请参阅随附的 link。
具体来说,我只想知道为什么在我的 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 和 os。
我使用 sizeof
函数检查没有最后填充的类型的大小,并使用 strideof
函数在末尾添加填充(strideof 在结构数组中更有用, Swift然后在末尾添加字节以达到对齐要求的下一个倍数。)
我知道对于小于或等于 8 字节大小的类型,填充是必要的。
但我不明白为什么在我的 64 位上内存对齐要求大于 8 个字节是有利的mac。
Float80 的值需要 80 位,即 10 个字节,6 个填充字节。
这是一张图片,可以更清楚地说明我的意思。 绿色 pos 允许 Float80,红色 pos 不允许。 这张图片中的内存是8字节块。
全部"primitive data types"(这个词可能有误,我的意思是 处理器使用的数据类型)有一个 "natural boundary", 并且编译器将相应地在内存中对齐它们。对齐方式 取决于处理器(例如 x86 或 ARM)和编程环境(例如 32 位与 64 位)。 一些处理器允许未对齐的数据(可能以较低的速度), 有些不允许。
对于 64 位 Intel 架构,要求列在 Data Alignment when Migrating to 64-Bit Intel® Architecture:
The 64-bit environment, however, imposes more-stringent requirements on data items. Misaligned objects cause program exceptions.
[...]
- Align 8-bit data at any address
- Align 16-bit data to be contained within an aligned four-byte word
- Align 32-bit data so that its base address is a multiple of four
- Align 64-bit data so that its base address is a multiple of eight
- Align 80-bit data so that its base address is a multiple of sixteen
- Align 128-bit data so that its base address is a multiple of sixteen
所以对齐不一定等于"word size",可以
更少或更多。 Float80
对应 "Extended Precision"
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 个字节,但 "stride" 为 12:
这保证在结构数组中所有元素都满足
相同的路线。
(注意SwiftstrideOf()
对应C的sizeof()
函数,这个在https://devforums.apple.com/message/1086107#1086107中有解释。)
Swift 字符串的声明显示为
struct String {
init()
}
但是我们凡人是看不到实际成员的。 在调试器中它看起来像这样:
表示它的成员是一个指针,
一个无符号字和另一个指针。所有这些类型都有大小和
在 64 位上对齐 8 个字节。这将解释大小(24 字节)
struct Swift
.
在 Martin R 的帮助下 link 以及它是处理器设计决策的提示。我找到了原因。
缓存行。
缓存线是处理器的非常小的内存,在我的 Intel Mac 64 位上它是 128 位(16 字节)。
从问题的图片中可以看出,我知道虚线和粗线之间存在差异。粗线位于处理器的高速缓存行之间。如果你可以用更多的内存成本做得更好,你不想加载 2 个缓存行。因此,如果处理器只允许,大小为 8 字节(或更大)的类型将在缓存行的开头对齐(每 16 的倍数)。对于与缓存行一样大的类型(在我的情况下是字大小的两倍,16 字节),不会有两次缓存行读取。正如您在图片中看到的,只有红色块穿过粗线(因此每个设计都不允许使用它们)。
有关详细信息,请参阅随附的 link。