gofmt 字节对齐

本文介绍了gofmt的一个扩展版本,它除了基本的格式化功能外,还能自动对Go代码中的struct进行字节对齐,以提升性能和代码可读性。文章详细解释了内存对齐的重要性,以及如何通过gofmt进行字节对齐操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

gofmt 自动字节对齐工具

用于替换 golang源码包中 gofmt 格式化的工具,可以实现自动对代码中struct进行字节对齐的功能。通过增加这一功能,可以使代码更加整洁、易读,提高代码质量和可维护性,并且通过字节对齐,达到节约内存的极致体验

  • 仓库地址 gofmt
  • gofmt 懂的都懂,本工具拥有与gofmt一模一样的功能,还增加了字节对齐格式化的能力,使用 -a 控制

为什么需要内存对齐

CPU 访问内存时,并不是逐个字节访问,而是以字长(word size)为单位访问。比如 32 位的 CPU ,字长为 4 字节,那么 CPU 访问内存的单位也是
4 字节。 这么设计的目的,是减少 CPU 访问内存的次数,提升 CPU 访问内存的吞吐量。比如同样读取 8 个字节的数据,一次读取 4
个字节那么只需要读取 2 次。

CPU 始终以字长访问内存,如果不进行内存对齐,很可能增加 CPU 访问内存的次数,例如:

在这里插入图片描述

变量 a、b 各占据 3 字节的空间,内存对齐后,a、b 占据 4 字节空间,CPU 读取 b 变量的值只需要进行一次内存访问。 如果不进行内存对齐,CPU
读取 b 变量的值需要进行 2 次内存访问。第一次访问得到 b 变量的第 1 个字节,第二次访问得到 b 变量的后两个字节。

从这个例子中也可以看到,内存对齐对实现变量的原子性操作也是有好处的,每次内存访问是原子的,如果变量的大小不超过字长,那么内存对齐后,
对该变量的访问就是原子的,这个特性在并发场景下至关重要。

内存对齐可以提高内存读写性能,并且便于实现原子性操作。

内存对齐规则

类型大小
bool1个字节
intN,uintN,floatN,comlexNN/8个字节(例如 float64是8个字节)
int,uint,uintptr1个字
*T1个字
string2个字(数据、长度)
[]T3个字(数据、长度、内容)
map1个字
func1个字
chan1个字
interface2个字(类型)

字长跟CPU相关,32位CPU一个字长就是4字节,64位CPU一个字长是8字节

示例

字节对齐之前的struct混乱无序(瞎**写的,忽略丑陋的命名)
type People struct {
    has         bool          //1
    Where       []int         //24
    MachineTime time.Time     // 24
    Name        string        // 16
    donot       interface{}   //16
    name        string        //16
    Age         int           // 8
    inte        uintptr       //8
    Loves       []int         // 24
    d           []int         //24
    sign        chan struct{} //8
    age         int           // 8
    a           int8          //1
    c           struct {
        a string
		c map[string]int
        b int32 // } haa struct {
    }          // 8+8+4 =20
    e []int    //24
    b struct{} // 0
}

func main() {
    fmt.Println("before sort", unsafe.Sizeof(People{}))  // 256
}
使用gofmt 进行格式化
gofmt -a -w file.go
按字节从大到小进行排序 ,空 struct 比较特殊,不占空间,放第一个最好
type People struct {
	b struct{} // 0
	c           struct {
		a string
		c map[string]int
		b int32 // } haa struct {
	} // 8+8+4 =20
	Loves       []int         // 24
	MachineTime time.Time     // 24
	d           []int         //24
	Where       []int         //24
	e []int    //24
	Name        string        // 16
	donot       interface{}   //16
	name        string        //16
	Age         int           // 8
	age         int           // 8
	inte        uintptr       //8
	sign        chan struct{} //8
	has         bool          //1
	a           int8          //1
}

// 排序后一个对象占用少了16个字节
func main() {
    fmt.Println("after sort", unsafe.Sizeof(People{}))  // 240
}

注意事项

  • 本工具仅支持 Golang 代码的字节对齐。
  • go version >=1.18
  • 对于对象引用类型不参与排序,置于底部,因为涉及到跨文件访问,这里没有对对象引用进行处理,可以个人略微计算一下进行调整

idea 如何使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

保存自动格式化了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂的程需猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值