为什么 go 经常将字符串的数据存储在未对齐的地址
Why is go often storing the data of strings at misaligned addresses
我已经阅读了很多关于内存中值对齐的重要性的信息,因为访问未对齐的地址可能会减慢操作速度或根本无法工作,具体取决于 CPU 体系结构(一个参考 https://syslog.ravelin.com/go-and-memory-layout-6ef30c730d51).但后来我注意到,在 Go 中实例化一个简单的字符串时,它经常将字符串值存储在未对齐的地址。
这个可以通过运行这段代码看出来:
package main
import (
"fmt"
"unsafe"
"reflect"
)
func main() {
testString:= "testString"
fmt.Println(fmt.Sprintf("Data is at address %d", ((*reflect.StringHeader)(unsafe.Pointer(&testString))).Data))
}
当 运行 它在 https://play.golang.org/p/d1eX0nP3AgV 上时,我不断收到:
Data is at address 1140305
1140305
显然不能被 4
或 8
整除。
有人可以解释一下为什么 Go 将该值存储在未对齐的地址吗?使用对齐的不是更好吗?这仅仅是为了不浪费 space,同时依赖于现代 CPUs 可以处理它的事实。还是因为虚拟内存层抽象了物理内存地址,而实际上物理地址是正确对齐的?
你说得对,32 位值(例如整数)应该在 4 字节边界上对齐,否则访问它可能需要两次内存访问而不是一次。类似地,64 位值应该在 8 字节边界上对齐,但在 32 位系统中(即 32 条数据线到内存)4 字节边界就足够了,因为无论如何都需要两次内存访问。
但是,Go 中的字符串数据实际上是字节数组,因此没有对齐要求。如果你在 C 中打印一个字符串的地址,你会发现同样的事情(出于效率原因,它非常关心对齐)。
一旦理解,对齐就相当简单,但需要大量解释。我在 http://devmethodologies.blogspot.com/2013/04/alignment-and-pragma-pack.html.
上写了它(针对 C)
我已经阅读了很多关于内存中值对齐的重要性的信息,因为访问未对齐的地址可能会减慢操作速度或根本无法工作,具体取决于 CPU 体系结构(一个参考 https://syslog.ravelin.com/go-and-memory-layout-6ef30c730d51).但后来我注意到,在 Go 中实例化一个简单的字符串时,它经常将字符串值存储在未对齐的地址。
这个可以通过运行这段代码看出来:
package main
import (
"fmt"
"unsafe"
"reflect"
)
func main() {
testString:= "testString"
fmt.Println(fmt.Sprintf("Data is at address %d", ((*reflect.StringHeader)(unsafe.Pointer(&testString))).Data))
}
当 运行 它在 https://play.golang.org/p/d1eX0nP3AgV 上时,我不断收到:
Data is at address 1140305
1140305
显然不能被 4
或 8
整除。
有人可以解释一下为什么 Go 将该值存储在未对齐的地址吗?使用对齐的不是更好吗?这仅仅是为了不浪费 space,同时依赖于现代 CPUs 可以处理它的事实。还是因为虚拟内存层抽象了物理内存地址,而实际上物理地址是正确对齐的?
你说得对,32 位值(例如整数)应该在 4 字节边界上对齐,否则访问它可能需要两次内存访问而不是一次。类似地,64 位值应该在 8 字节边界上对齐,但在 32 位系统中(即 32 条数据线到内存)4 字节边界就足够了,因为无论如何都需要两次内存访问。
但是,Go 中的字符串数据实际上是字节数组,因此没有对齐要求。如果你在 C 中打印一个字符串的地址,你会发现同样的事情(出于效率原因,它非常关心对齐)。
一旦理解,对齐就相当简单,但需要大量解释。我在 http://devmethodologies.blogspot.com/2013/04/alignment-and-pragma-pack.html.
上写了它(针对 C)