码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 【go实战系列四】go语言中 Hot path 热路径 从sync.Once去理解如何设计我们自己的 struct


    历史go篇章

    【go实战系列一】开篇:在循环中重新定义变量(redefining for loop variable semantics)
    【go实战系列二】关于切片的基本操作 copy sort append 快速复制 排序 删除操作

    【go实战系列三】关于切片的基本操作 copy sort append 快速复制 排序 删除操作
    【go实战系列四】go语言中 Hot path 热路径 可能你从没听过 却如此重要


    文章目录

    • 历史go篇章
    • 前言
    • 一、sync.Once
      • 1、sync.Once 与 init()区别
      • 2、sync.Once 的原理
    • 二、Hot path 热路径
      • 1、 done 为什么是第一个字段
      • 2、我们定义一个 struct 查看
      • 3、结论:结构体第一个字段的地址和结构体的指针是相同的
      • 4、疑问:为什么 done 的类型为uint32?
    • 总结


    前言

    这是根据go在项目实战中,作者发掘的问题与技巧,希望能与所有的gopher一起分享,一起成长,如果文章有错误,也请大家及时指正问题,作者会立刻修改


    一、sync.Once

    1、sync.Once 与 init()区别

    sync.Once 是 Go 标准库提供的使函数只执行一次的实现,常应用于单例模式,例如初始化配置、保持数据库连接等。作用与 init 函数类似,但有区别。

    init 函数是当所在的 package 首次被加载时执行,若迟迟未被使用,则既浪费了内存,又延长了程序加载时间。

    sync.Once 可以在代码的任意位置初始化和调用,因此可以延迟到使用时再执行,并发场景下是线程安全的。
    在多数情况下,sync.Once 被用于控制变量的初始化,这个变量的读写满足如下三个条件:

    • 当且仅当第一次访问某个变量时,进行初始化(写)
    • 变量初始化过程中,所有读都被阻塞,直到初始化完成
    • 变量仅初始化一次,初始化完成后驻留在内存里

    2、sync.Once 的原理

    第一:保证变量仅被初始化一次,需要有个标志来判断变量是否已初始化过,若没有则需要初始化。
    第二:线程安全,支持并发,无疑需要互斥锁来实现。

    go-sdk 实现源码如下:

    package sync
    
    import (
        "sync/atomic"
    )
    
    type Once struct {
        done uint32
        m    Mutex
    }
    
    func (o *Once) Do(f func()) {
        if atomic.LoadUint32(&o.done) == 0 {
            o.doSlow(f)
        }
    }
    
    func (o *Once) doSlow(f func()) {
        o.m.Lock()
        defer o.m.Unlock()
        if o.done == 0 {
            defer atomic.StoreUint32(&o.done, 1)
            f()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    sync.Once 的实现与一开始的猜测是一样的,使用 done 标记是否已经初始化,使用锁 m Mutex 实现线程安全。

    二、Hot path 热路径

    1、 done 为什么是第一个字段

    字段 done 的注释也非常值得一看:

    type Once struct {
        // done indicates whether the action has been performed.
        // It is first in the struct because it is used in the hot path.
        // The hot path is inlined at every call site.
        // Placing done first allows more compact instructions on some architectures (amd64/x86),
        // and fewer instructions (to calculate offset) on other architectures.
        done uint32
        m    Mutex
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    其中解释了为什么将 done 置为 Once 的第一个字段:done 在热路径中,done 放在第一个字段,能够减少 CPU 指令,也就是说,这样做能够提升性能。

    2、我们定义一个 struct 查看

    package main
    
    import (
    	"fmt"
    )
    
    type student struct {
    	name string
    	age  int
    }
    
    func main() {
    	p1 := &student{
    		name: "123",
    		age:  123,
    	}
    
    	fmt.Printf("%p \n", p1)
    	fmt.Printf("%p \n", &p1.name)
    	fmt.Printf("%p \n", &p1.age)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    打印结果:
    0xc000008078
    0xc000008078
    0xc000008088

    3、结论:结构体第一个字段的地址和结构体的指针是相同的

    热路径(hot path)是程序非常频繁执行的一系列指令,sync.Once 绝大部分场景都会访问 o.done,在热路径上是比较好理解的,如果 hot path 编译后的机器码指令更少,更直接,必然是能够提升性能的。

    为什么放在第一个字段就能够减少指令呢?
    因为结构体第一个字段的地址和结构体的指针是相同的,如果是第一个字段,直接对结构体的指针解引用即可。如果是其他的字段,除了结构体指针外,还需要计算与第一个值的偏移(calculate offset)。在机器码中,偏移量是随指令传递的附加值,CPU 需要做一次偏移值与指针的加法运算,才能获取要访问的值的地址。因为,访问第一个字段的机器代码更紧凑,速度更快。

    4、疑问:为什么 done 的类型为uint32?

    type Once struct {
        // done indicates whether the action has been performed.
        // It is first in the struct because it is used in the hot path.
        // The hot path is inlined at every call site.
        // Placing done first allows more compact instructions on some architectures (amd64/x86),
        // and fewer instructions (to calculate offset) on other architectures.
        done uint32
        m    Mutex
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    欢迎大家在评论区指出这个问题的答案

    总结

    参考:What does “hot path” mean in the context of sync.Once?
    希望对大家能够有帮助

  • 相关阅读:
    SpringBoot接口中如何直接返回图片数据
    scala中函数名 _(下划线)如何理解?
    【pytorch笔记】第五篇 torchvision,Dataloader,nn.Module的使用
    一些计算机的冷知识,你都知道吗?
    微服务架构
    【计组】处理器
    SpringCloud 02 Rest学习环境搭建(DeptProvider)
    springboot中如何进行业务层测试事务回滚
    求你了,别在高并发场景中使用悲观锁了!
    CDA数据分析——AARRR增长模型的介绍、使用
  • 原文地址:https://blog.csdn.net/wanglei19891210/article/details/127997533
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | 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号