go 7年前

GO内存对齐

作者头像 刘宇帅
3427 0

内存对齐的作用

  1. 平台原因(移植):不是所有的硬件平台都可以访问任意位置上的任意数据的,有些硬件只能在特定位置取特定数据。
  2. 性能问题:经过内存对齐,CPU的内存访问速度会提升。因为对齐的元素只需要一次内存访问,未对齐的需要两次。

    性能问题

    一般程序员会认为内存如下图所示,是有一个个的字节组成,而CPU却不是这样看待的。

CPU把内存当作一块一块的,块的大小可以是2、4、8、16字节大小,因此CPU读取内存是一块一块读取的。(块的大小称为内存读取粒度

假设CPU要读取一个int型4字节大小的数据,分两种情况讨论:

  1. 数据从0字节开始
  2. 数据从1字节开始 假设内存读取粒度为4:

当数据从0字节开始时,CPU只需要读取一次内存即可取到数据。
当数据从1字节开始时,CPU读取数据就变得复杂了,因为数据没有在一个块内存储。这种情况下,CPU先访问内存读取0-3字节数据,在访问内存读取4-7字节数据,然后把0、5、6、7字节的数据去掉,合并1、2、3、4字节数据。这样的操作显然是浪费了很多性能,所以就有了字节对齐,以空间换时间。

内存对齐规则

默认对齐长度:编译器会有一个默认对齐长度,golang 中 64 为系统默认的对齐长度是 8 (这个默认值规则具体不清楚,后面再来补充)

  1. 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的起始位置必须是默认对齐长度和该数据成员长度中最小的长度的倍数。
  2. 除了结构成员需要对齐,结构本身也需要对齐,结构的长度必须是编译器默认的对齐长度和成员中最长类型中最小的数据大小的倍数对齐。

    GO类型对齐

    package main
    import (
    "fmt"
    "reflect"
    )
    type Data struct {
    b byte
    a int32
    x int64
    }
    type Data1 struct {
    b byte
    x int64
    a int32
    }
    func main() {
    var d Data
    t := reflect.TypeOf(d)
    fmt.Println(t.Size(),t.Align())
    var d1 Data
    t1 := reflect.TypeOf(d1)
    fmt.Println(t1.Size(),t1.Align())
    }
    ----------
    输出结果:
    16 8
    24 8

    上面的内存结构如下所示:

    
    d 的内存结构:
        b---|aaaa|xxxx|xxxx

d1的内存结构: b---|----|xxxx|xxxx|aaaa|----

## C 语言
C 语言对齐规则和 golang 一致,C 语言的默认对齐大小由编译器决定,我们也可以通过以下代码修改默认对齐大小。

pragma pack(2) // 修改默认对齐大小为 2

作者头像

刘宇帅

非著名程序员,全栈开发工程师,长期专注系统开发与架构设计。

提示

功能待开通!


暂无评论~

相关文章

震惊!同事小张踩了 gorm 神奇的 Scan 函数的坑

gorm 简介 gorm 是 go 语言中实现的比较好的 ORM 包,且是国人开发的。项目地址 事故描述 Scan 是 gorm 提供的一个把数据库结果读取到 struct 的函数。定义如下: // Scan scan value to a struct func (s *DB) Scan(dest interface{}) *DB { return s.NewScope(s.Value).Set("gorm:query_destination", dest).callCallbacks(s.parent.callbacks.queries).db } 今天同事小张写代码的时候写了一个

goland集成fmt goimports gometalinter

三个工具介绍 go fmt是用来规范go文件格式,比如格式化单个文件 go fmt xxx.go goimports 用来检查导入包,导入依赖包,删除不依赖的包 gometalinter 集成go语言几乎所有检测工具,静态分析代码,包含功能如下 go vet -工具可以帮我们静态分析我们的源码存在的各种问题,例如多余的代码,提前return的逻辑,struct的tag是否符合标准等。 go tool vet --shadow -用来检查作用域里面设置的局部变量名和全局变量名设置一样导致全局变量设置无效的问题 gotype -类型检测用来检测传递过来的变量和预期变量类型一致 gotype -x

Golang 本地编译和交叉编译

Go 语言的可移植性 Java 平台可移植性是众所都知的,Java 的可移植性依赖于其虚拟机 JVM,Java 实现了对不同平台的 JVM 的支持,那么一份 Java 代码就可以在各个平台上运行。而 Go 语言的可移植性也是依赖于其 runtime,runtime 去对接操作系统层,用户代码在 runtime 中运行,用户代码就不用去关心平台问题。 查看 Go 支持的OS和平台: > $ go version go version go1.11 darwin/amd64 liushuai@liushuaideMacBook-Pro  ~/Documents/goProject/src

[转]Golang中JSON使用小技巧

临时忽略掉struct中空字段 type User struct { Email string `json:"email"` Password string `json:"password"` } 当我们把用户信息返回给前端的时候显然需要忽略调Password 字段,则可以这样做: json.Marshal(struct{ *User Password bool `json:"password,omitempty"` }{ User:user, }) 临时添加额外字段 type User struct { Email string `json:"

golang 中丰富的字符串格式化

golang中字符串格式化输出 package main import ( "fmt" "os" ) type point struct { x, y int } func main() { // Go提供了几种打印格式,用来格式化一般的Go值,例如 // 下面的%v打印了一个point结构体的对象的值 p := point{1, 2} fmt.Printf("%v\n", p) // 如果所格式化的值是一个结构体对象,那么`%+v`的格式化输出 // 将包括结构体的成员名称和值 fmt.Printf("%