geektutu / blog

极客兔兔的博客,Coding Coding 创建有趣的开源项目。
https://geektutu.com
Apache License 2.0
166 stars 21 forks source link

Go struct 内存对齐 | Go 语言高性能编程 | 极客兔兔 #118

Open geektutu opened 3 years ago

geektutu commented 3 years ago

https://geektutu.com/post/hpg-struct-alignment.html

Go 语言/golang 高性能编程,Go 语言进阶教程,Go 语言高性能编程(high performance go)。本文介绍了结构体(struct)占用的内存空间如何计算,为什么要字节对齐/内存对齐,Go 语言中的大小和对齐保证(size and alignment guarantees),字节对齐和安全访问(原子访问)的关系,以及如果利用内存对齐的规律减小 struct 内存占用。

OhYee commented 3 years ago

如果内存对齐只是涉及更改顺序的话,go 为什么不在编译时和运行时自动进行对齐呢? 如果编译时存在跨平台优化问题,那么运行时应该可以确定如何对齐最优吧。

zhcoders commented 3 years ago

@OhYee 如果内存对齐只是涉及更改顺序的话,go 为什么不在编译时和运行时自动进行对齐呢? 如果编译时存在跨平台优化问题,那么运行时应该可以确定如何对齐最优吧。

Go在编译的时候会自动内存对齐,就是上面说的,不同顺序占用的内存不一样,这就是自动对齐的结果。Go如果更改了结构体的顺序,如果在数据传输的时候,如何正确的读取到结构体的内容呢?

OhYee commented 3 years ago

@zhcoders 明白了,没考虑到跨平台数据传输的问题,感谢 👍

wathenjiang commented 3 years ago

字节对齐这个概念有两处需要强调:

  1. CPU 只从对齐的地址开始加载数据(这一点可能遗漏了)
  2. CPU 读取块的大小是固定的,通常为 B 的 2 的整数幂次
qilinworld commented 3 years ago

“b 是第二个字段,对齐倍数为 2,因此,必须空出 1 个字节,偏移量才是 2 的倍数,从第 2 个位置开始占据 2 字节。” 为什么要空一个字段呢?

NoahNye commented 3 years ago

@qilinworld “b 是第二个字段,对齐倍数为 2,因此,必须空出 1 个字节,偏移量才是 2 的倍数,从第 2 个位置开始占据 2 字节。” 为什么要空一个字段呢?

为了减少CPU读取次数,是一种空间换时间的做法。

比如demo1,按照2的倍数,第一次读取a(实际占用1字节)读两个字节,第二次读取b(实际占用2字节)也是两个字节,第三次读取c(实际占用4个字节)前两个字节,第四次读取c后两个字节,共读取4次。

demo2也是,按照c的倍数4对齐,第一次读取a(实际占用1字节)四个字节,第二次读取c四个字节,第三次读取b(实际占用2字节)四个字节,共读取3次。

如果按照a的1倍对齐,那么demo1里CPU就要读取a(1次)+b(2次)+c(3次)=6次,demo2也一样。

但我在本机看到demo1和demo2的对齐倍数都是4

HeliumTang commented 3 years ago

对于 struct 结构体类型的变量 x,计算 x 每一个字段 f 的 unsafe.Alignof(x.f),unsafe.Alignof(x) 等于其中的最大值。 根据上面的规则,demo1中的b字段的对齐:b 是第二个字段,对齐倍数为 2,因此,必须空出 1 个字节,偏移量才是 2 的倍数,从第 2 个位置开始占据 2 字节。 应该将对齐倍数看做4才对吧?

livedrm commented 2 years ago

空结构体填充不是字节对齐的问题

jeffreyyjp commented 2 years ago

"即当 struct{} 作为结构体最后一个字段时,需要内存对齐。因为如果有指针指向该字段, 返回的地址将在结构体之外,如果此指针一直存活不释放对应的内存,就会有内存泄露的问题(该内存不因结构体释放而释放)。"

这段话不太理解,“因为如果有指针指向该字段, 返回的地址将在结构体之外”,是对齐的时候,还是非对齐的时候产生此现象。以及为什么返回的地址在结构体之外?能否举个具体的例子呢?

haima96 commented 2 years ago

@HeliumTang 对于 struct 结构体类型的变量 x,计算 x 每一个字段 f 的 unsafe.Alignof(x.f),unsafe.Alignof(x) 等于其中的最大值。 根据上面的规则,demo1中的b字段的对齐:b 是第二个字段,对齐倍数为 2,因此,必须空出 1 个字节,偏移量才是 2 的倍数,从第 2 个位置开始占据 2 字节。 应该将对齐倍数看做4才对吧?

到b字段的时候最大值是b只占用2字节没问题的,到c字段最大值为c占用4字节没问题的

dablelv commented 2 years ago

博主,请问您的这个博客站点是如何搭建的?

conlanwl commented 2 years ago

有联系方式吗

ming194 commented 1 year ago

兔兔, 这个地方感觉有点模糊,

  1. 泄露的是哪块内存,
  2. 为什么补齐之后就不会发生泄露了

因为如果有指针指向该字段, 返回的地址将在结构体之外,如果此指针一直存活不释放对应的内存,就会有内存泄露的问题(该内存不因结构体释放而释放)。

  1. 我理解的是返回的地址指的是空结构体的地址, 也就是zerobase
  2. 如果1成立, 那么有很大概率这个地址会永远存在, 因为这个地址是所有空值公用的
  3. 如果不是zerobase, 那返回的是什么地址
abbothzhang commented 1 year ago

我用fieldalignment检测,并没有提示demo2需要优化,是什么原因呢