gofmt 字节对齐

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 如何使用

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

保存自动格式化了

  • 21
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在C语言中,结构体的字节对齐是为了优化内存访问速度和对齐要求而进行的一种对齐方式。结构体的字节对齐确保结构体中的成员按照一定的规则进行排列,以便于处理器高效地访问内存。 结构体的字节对齐规则通常由编译器根据特定的对齐选项和目标平台的要求来确定。在C语言中,可以使用`#pragma pack`指令或者编译器提供的特定选项来控制结构体的字节对齐方式。 默认情况下,大多数编译器会按照特定的对齐规则进行字节对齐。这些规则通常是根据基本数据类型的大小来确定的。例如,常见的对齐规则是按照4字节对齐(即结构体成员的偏移量必须是4的倍数)或者8字节对齐。 以下是一个示例,展示了如何使用`#pragma pack`指令来设置结构体的字节对齐方式: ```c #pragma pack(push, 1) // 以1字节对齐 struct MyStruct { char c; int i; double d; }; #pragma pack(pop) // 恢复默认的对齐方式 int main() { printf("sizeof(MyStruct) = %zu\n", sizeof(struct MyStruct)); return 0; } ``` 在上面的示例中,`#pragma pack(push, 1)`指令将当前的对齐方式推入一个栈中,并将对齐方式设置为1字节对齐。然后定义了一个包含不同类型成员的结构体。最后,使用`#pragma pack(pop)`指令将对齐方式恢复为默认值。 请注意,修改结构体的字节对齐方式可能会导致内存浪费或者访问错误,因此在修改字节对齐方式时要特别小心。建议仅在必要时进行修改,并确保了解目标平台的字节对齐要求。 希望这能回答你的问题!如果还有疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

疯狂的程需猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值