美文网首页
为什么要内存对齐?

为什么要内存对齐?

作者: 哥斯拉啊啊啊哦 | 来源:发表于2020-09-05 15:46 被阅读0次

一. 什么是内存对齐(Memory alignment),也叫字节对齐

在计算机中,内存是按 字节(byte, 1byte = 8bit) 划分的,而cpu在读取内存数据时,并不是一字节一字节读取的。实际上是按 块 来读取的。
块的大小可以是1,2,4,8,16等等,这块的大小也称为 内存访问粒度
内存对齐 是将特定的数据类型按照一定的规则摆放在内存上,以此 提高cpu访问内存的速度

看如下示例:

如上图,内存访问颗粒度为4,数据大小为4字节的场景:

场景1:做了内存对齐,index 2-3 补充空字节,这样 数据1,数据2 都刚好只存在一个内存块中,
读取数据时,一次就能将数据读取完毕

场景2:没有内存对齐,数据2 一部分存在内存块1中,一部分存在内存块2中。
读取数据2时,需要将块1的数据0-3读取出来,丢弃0-1字节数据,
再读取块2的数据4-7,丢弃6-7字节的数据,
再组合 2,3,4,5字节才能得到数据2

总结:很明显,场景2读取数据比场景1繁琐许多。
如果不做内存对齐,cpu在读取内存数据时,会增加许多耗时的动作。
而做了内存对齐后,虽然会产生一部分内存碎片,但极大提高了cpu访问内存数据的速度,属于空间换时间的做法

提高访问速度是内存对齐的原因之一,另外一个原因是某些平台(arm)不支持未内存对齐的访问


了解了内存对齐的原理以及优缺点后,下面将内存对齐在实际编程中的应用。
用下面的例子来说明:

// 写法1
type Part1 struct {
    a int8
    b int64
    c int16
}

// 写法2
type Part2 struct {
    a int8
    b int16
    c int64
}

func main() {
    var (
        pBool       bool       = false
        pByte       byte       = '0'
        pSting      string     = "hello"
        pInt8       int8       = 1
        pInt16      int16      = 1
        pInt32      int32      = 1
        pInt64      int64      = 1
        pfloat32    float32    = 1
        pfloat64    float64    = 1
        pComplex64  complex64  = 1
        pComplex128 complex128 = 1
        part1                  = Part1{}
        part2                  = Part2{}
    )

    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pBool, unsafe.Sizeof(pBool), unsafe.Alignof(pBool))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pByte, unsafe.Sizeof(pByte), unsafe.Alignof(pByte))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pSting, unsafe.Sizeof(pSting), unsafe.Alignof(pSting))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pInt8, unsafe.Sizeof(pInt8), unsafe.Alignof(pInt8))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pInt16, unsafe.Sizeof(pInt16), unsafe.Alignof(pInt16))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pInt32, unsafe.Sizeof(pInt32), unsafe.Alignof(pInt32))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pInt64, unsafe.Sizeof(pInt64), unsafe.Alignof(pInt64))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pfloat32, unsafe.Sizeof(pfloat32), unsafe.Alignof(pfloat32))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pfloat64, unsafe.Sizeof(pfloat64), unsafe.Alignof(pfloat64))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pComplex64, unsafe.Sizeof(pComplex64), unsafe.Alignof(pComplex64))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", pComplex128, unsafe.Sizeof(pComplex128), unsafe.Alignof(pComplex128))
    fmt.Println()
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", part1, unsafe.Sizeof(part1), unsafe.Alignof(part1))
    fmt.Printf("%10T 类型 所占字节数量 =%2d, 对齐保证系数=%2d\n", part2, unsafe.Sizeof(part2), unsafe.Alignof(part2))
}
----------------------------------------------------------------------------------------------------------
运行结果如下:
      bool 类型 所占字节数量 = 1, 对齐保证系数= 1
     uint8 类型 所占字节数量 = 1, 对齐保证系数= 1
    string 类型 所占字节数量 =16, 对齐保证系数= 8
      int8 类型 所占字节数量 = 1, 对齐保证系数= 1
     int16 类型 所占字节数量 = 2, 对齐保证系数= 2
     int32 类型 所占字节数量 = 4, 对齐保证系数= 4
     int64 类型 所占字节数量 = 8, 对齐保证系数= 8
   float32 类型 所占字节数量 = 4, 对齐保证系数= 4
   float64 类型 所占字节数量 = 8, 对齐保证系数= 8
 complex64 类型 所占字节数量 = 8, 对齐保证系数= 4
complex128 类型 所占字节数量 =16, 对齐保证系数= 8

main.Part1 类型 所占字节数量 =24, 对齐保证系数= 8
main.Part2 类型 所占字节数量 =16, 对齐保证系数= 8
----------------------------------------------------------------------------------------------------------
总结:
如果一个类型 'T' 的对齐保证系数为 'N(正整数)',则该类型 'T' 的内存占用字节数必须是 'N' 的整数倍

在golang语言中,可以用 'unsafe.Sizeof(T)' 来获取类型T的所占字节数量;
用 ' unsafe.Alignof(T)' 来获取类型T的对齐保证系数;
golang的内存对齐保证系数有1,2,4,8,最大为8

如上示例:结构体part1,part2 拥有相同的字段,而字段顺序不同。
结果占用的字节数 part1为24,part2为16。part2更省内存空间,为什么会这样?
原因是 part1 花费了更多的空间去对齐内存,如下图示:

如上图所示:
由于 类型 'T' 的内存字节数必须是 内存对齐系数'N' 的整数倍
part1 用了 13 个字节去对齐内存,1+2+8+13 = 24
part2 用了 5 个字节去对齐内存,1+2+8+5 = 16

因此,在实际工作中定义结构体时,应调整好字段类型的先后顺序,使得结构体占用更小的内存空间,
这样既减少了内存碎片,还提高了服务性能。

至于具体的优化规则我没能总结出来,从大往小排列或者从小往大排列都不能涵盖所有情况,
感觉要具体情况具体分析

相关文章

  • C/C++内存对齐

    在面试或工作中,经常会遇到内存对齐的问题。这里结合我的理解谈一谈对内存对齐的理解。 1. 为什么要内存对齐,不对齐...

  • 结构体内存对齐

    对象内存对齐 探讨的问题 1.什么是内存对齐?2.为什么要做内存对齐?3.结构体内存对齐规则4.源码内存对齐算法 ...

  • 2.iOS底层学习之内存对齐

    学习了内存对齐之后的疑问?? 1.为啥要内存对齐?2.内存对齐的规则?3.内存对齐实例分析。 内存对齐的目的 上网...

  • OC底层原理三:内存对齐分析

    获取内存大小 上一篇我们简单的提了下内存字节对齐以及为什么要内存字节对齐,那么我们首先看下有什么方式可以获取内存大...

  • 为什么要内存对齐?

    一. 什么是内存对齐(Memory alignment),也叫字节对齐 在计算机中,内存是按 字节(byte, 1...

  • 内存对齐

    在C语言柔性数组一文中,提到了内存对齐,于是想写篇文章总结总结内存对齐。 内存对齐 为什么需要内存对齐 计算机系统...

  • 结构体内存对齐

    为什么要进行内存对齐 1.为了cpu读取更快使CPU的访问内存数据的速度加快,如果一个内存的数据是经过对齐的,那么...

  • 内存对齐

    内存对齐 为什么是16,而不是9呢?这就是因为内存对齐的原因 为什么需要内存对齐 计算机系统对基本数据类型的合法地...

  • IOS底层探索-内存对齐

    这篇文章我们来讲讲关于内存对齐的一些趣事 为什么要内存对齐 平台原因(移植原因):不是所有的硬件平台都能访问任意地...

  • 内存对齐

    为什么需求内存对齐 为了访问未对齐的内存,处理器需要做两次内存访问; 对齐的内存访问仅需要一次访问 什么数据需要内...

网友评论

      本文标题:为什么要内存对齐?

      本文链接:https://www.haomeiwen.com/subject/aqlfektx.html