【问题标题】:Why is go often storing the data of strings at misaligned addresses为什么 Go 经常将字符串的数据存储在未对齐的地址
【发布时间】:2020-03-06 22:00:15
【问题描述】:

我已经阅读了很多关于内存中值对齐的重要性的文章,因为访问未对齐的地址可能会减慢操作速度,或者根本不工作,具体取决于 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 将该值存储在一个未对齐的地址中?使用对齐的不是更好吗?这仅仅是为了不浪费空间,同时依靠现代 CPU 可以处理它的事实。还是因为虚拟内存层抽象了物理内存地址,实际上物理地址是正确对齐的?

【问题讨论】:

  • 您确定在真实硬件上运行时会发生这种情况吗?我有一种感觉,只是因为它是在操场上的 nacl 上运行的。
  • 是的,在我的工作站上运行时(Windows 10 和英特尔 i9 上的 WSL2)我得到了我实例化的前 4 个字符串的这些数据地址:4997046 4997057 4997068 4997079
  • 哦,是的,它是数据指针本身。也许在大多数情况下都没有关系,因为字符串数据的长度是任意的?由于字符串数据只是一系列单独的字节,规范只保证对齐为 1。
  • 可能,你指的是哪个规范?

标签: go memory-management memory-layout


【解决方案1】:

您是对的,32 位值(例如整数)应该在 4 字节边界上对齐,否则访问它可能需要两次内存访问而不是一次。类似地,64 位值应该在 8 字节边界上对齐,但在 32 位系统(即 32 条数据线到内存)中,4 字节边界就足够了,因为无论如何都需要两次内存访问。

然而,Go 中的字符串数据实际上是一个字节数组,因此没有对齐要求。如果您在 C 中打印字符串的地址,您会发现同样的事情(出于效率原因,它非常关心对齐)。

一旦你理解了对齐,它就相当简单,但需要大量解释。我在http://devmethodologies.blogspot.com/2013/04/alignment-and-pragma-pack.html 写了关于它(用于C)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-23
    • 2018-05-02
    • 1970-01-01
    • 1970-01-01
    • 2011-03-28
    相关资源
    最近更新 更多