码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Golang 优化之内存对齐


    这里填写标题

    • 1. Golang 优化之内存对齐
      • 1.1. 前文
      • 1.2. 正文
        • 1.2.1. 什么是内存对齐?
        • 1.2.2. 为什么需要内存对齐?
          • 1.2.2.1. 平台原因
          • 1.2.2.2. 性能原因
      • 1.3. 如何进行内存对齐?
      • 1.4. golang 的内存对齐如何体现?
        • 1.4.1. 结构体的相同成员不同顺序
        • 1.4.2. 指针运算
      • 1.5. 如何利用内存对齐来优化 golang
        • 1.5.1. 结构体占用内存过大的问题
        • 1.5.2. 指针运算的坑

    1. Golang 优化之内存对齐

    1.1. 前文

    话说今天在用 uintptr 进行指针运算的时候, 突然想起来有个内存对齐的东西, 那么对这个 uintptr 计算是否会有影响?

    带着疑问, 开始吧。

    你将获得以下知识点:

    1. 什么是内存对齐?
    2. 为什么需要内存对齐?
    3. 如何进行内存对齐?
    4. golang 的内存对齐如何体现?
    5. 如何利用内存对齐来优化 golang?

    1.2. 正文

    1.2.1. 什么是内存对齐?

    在想象中内存应该是一个一个独立的字节组成的。像这样:

    事实上, 人家是这样的:

    内存是按照成员的声明顺序, 依次分配内存, 第一个成员偏移量是 0, 其余每个成员的偏移量为指定数的整数倍数 (图中是 4)。像这样进行内存的分配叫做内存对齐。

    1.2.2. 为什么需要内存对齐?

    原因有两点:

    1.2.2.1. 平台原因

    并不是所有的硬件平台都能访问任意地址上的任意数据, 会直接报错的!
    (解释: 比如说有的 cpu 读取 4 个字节数据, 要是没有内存对齐, 从 1 开始那么内存就需要把 0-7 字节的全部取出来, 再剔除掉 1/5/6/7, 增加了额外的操作, cpu 不一定能这么搞, 自然就报错了)

    1.2.2.2. 性能原因

    访问未对齐的内存, 需要访问两次; 如果对齐的话就只需要一次了。
    (解释: 比如取 int64, 按照 8 个位对齐好了, 那获取的话直接就是获取 8 个字节就好了, 边界好判断)

    1.3. 如何进行内存对齐?

    二个原则:

    1. 具体类型, 对齐值 = min(编译器默认对齐值, 类型大小 Sizeof 长度)
    2. struct 每个字段内部对齐, 对齐值 = min(默认对齐值, 字段最大类型长度)

    1.4. golang 的内存对齐如何体现?

    1.4.1. 结构体的相同成员不同顺序

    结构体是平时写代码经常用到的。相同的成员, 不同的排列顺序, 会有什么区别吗?

    举个例子:

    func main() {
    	fmt.Println(unsafe.Sizeof(struct {
    		i8  int8
    		i16 int16
    		i32 int32
    	}{}))
    	fmt.Println(unsafe.Sizeof(struct {
    		i8  int8
    		i32 int32
    		i16 int16
    	}{}))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出:

    1   8
    2   12
    
    • 1
    • 2

    what? 竟然不一样。

    分析一波: 需要内存对齐的话, 因为最大是 int32, 所以最终记过必须是 4 个字节的倍数才能对齐。

    当 8-16-32 的时候, 类似这样 |x-xx|xxxx|。

    当 8-32-16 的时候, 类似这样 |x—|xxxx|xx–|。

    一眼就看出了大小了。

    这里的为什么是 x-xx 而不是 xxx- 需要说明下。因为当 int8 放入内存的时候, 其占坑 1 个字节, 对齐值为 1, 而 int16 占坑 2 个字节, 对齐值为 2, 所以说会先偏移 2 个字节从第三个字节才开始放 int16 的数

    1.4.2. 指针运算

    现在对结构体 Test 通过指针计算的方式进行赋值。

    Test 内存情况: |x-xx|xxxx|。需要注意的是这里的 - 需要多计算一个字节才行。

    type Test struct {
    	i8  int8
    	i16 int16
    	i32 int32
    }
    
    func main() {
    	var t = new(Test)
    	// 从 0 开始
    	var i8 = (*int8)(unsafe.Pointer(t))
    	*i8 = int8(10)
    	
    	// 偏移 int8+1 的字节数, 注意这里有个 1! ! !  
    	var i16 = (*int16)(unsafe.Pointer(uintptr(unsafe.Pointer(t))+ uintptr(1)  + uintptr(unsafe.Sizeof(int8(0)))))
    	*i16 = int16(10)
    
    	// 偏移 int8+1+int16 + 的字节数, 注意这里有个 1! ! !  
    	var i32 = (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) + uintptr(1) + uintptr(unsafe.Sizeof(int8(0))+uintptr(unsafe.Sizeof(int16(0))))))
    	*i32 = int32(10)
    	fmt.Println(*t)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    输出:

    1 | {10 10 10}
    
    • 1

    附上两个神器:

    功能函数
    获取对齐值unsafe.Alignof(t.i16)
    获取偏移值unsafe.Offsetof(t.i16)

    1.5. 如何利用内存对齐来优化 golang

    1.5.1. 结构体占用内存过大的问题

    根据计算对齐值进行成员顺序的拼凑, 可以一定程度上缩小结构体占用的内存。

    1.5.2. 指针运算的坑

    通过分析偏移量和对齐值, 准确计算每个成员所偏移的位数, 避免算错。

  • 相关阅读:
    springboot网络安全考核平台毕业设计源码
    动手写prometheus的exporter-04-Summary(摘要)
    自动化测试框架Playwright安装以及使用
    Java实现整数互转罗马数字基本算法
    在UniApp的H5项目中,生成二维码和扫描二维码的操作处理
    538页21万字数字政府大数据云平台项目建设方案
    OpenAI 再放大招,ChatGPT 正式版 API 就是救世主尼奥,而我们都是愚蠢的人类
    Word控件Spire.Doc 【段落处理】教程(八):如何在 C#、VB.NET 中的 Word 中创建多级列表编号
    【广州华锐互动】VR全景工厂虚拟导览,虚拟现实技术提升企业数字化信息管理水平
    再战SDRAM与资料整理。
  • 原文地址:https://blog.csdn.net/wan212000/article/details/128079317
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号