Go 语言性能优化指南

news2024/11/15 15:31:52

编写高性能的 Go 程序~

前言:

继上次课程的高质量编程内容讲解,本次课程主要介绍了在满足正确性、可靠性、健壮性、可读性等质量因素的前提下提高程序效率的性能优化建议;性能优化分析工具;以及性能调优的实战案例,分享了业务优化、基础库优化和 Go 语言优化的流程和方式 …

image.png


性能优化建议

Go 语言提供了支持基准性能测试的 Benchmark 工具,指令如下:

go test -bench=. -benchmem

💻对下面的代码执行测试指令:

// from fib.go
func Fib(n int) int {
    if n < 2 {
	return n
    }
    return Fib(n - 1) + Fib(n - 2)
}

// from fib_test.go
func BenchmarkFib10(b *testing.B) {
    // run the Fib function b.N times
    for n := 0; n < b.N; n++ {
	Fib(10)
    }
}

image.png

  • PS:使用 Benchmark 工具对优化前后的代码进行测试,评估代码性能。

性能优化的前提是满足正确可靠、简洁清晰等质量因素,性能优化是综合评估,有时候实践效率和空间效率可能对立,下面针对 Go 语言特性,给出了 Go 相关的性能优化建议。

优化建议 - Slice:

切片的本质是一个数组片段的描述,包含以下三项:

  • 数组指针 array unsafe.Pointer
  • 片段的长度 len
  • 片段的容量 cap (不改变内存分配情况下的最大长度)

尽可能在使用 make() 初始化切片时提供容量信息。这是因为向切片中添加的元素数量超过默认容量会触发扩容机制,扩容是一个比较耗时的操作。

image.png

func PreAlloc(size int) {
    data := make([]int, 0, size)
    for k:= 0; k < size; k++ {
        data = append(data, k)
    }
}

🎈切片使用陷阱:大内存未释放

  • 场景:
    • 原切片较大,代码在原切片基础上新建小切片。
    • 原底层数组在内存中有引用,得不到释放。

这是由于 Golang 中在已有切片的基础上创建切片,不会创建新的底层数组,而是直接复用原来的。如果只是需要用到其中的一小部分,复用原来的整个数组会导致占用较大的内存空间,建议使用 copy 替代 re-slice。

// re-slice,占用空间较大:
func GetLastBySlice(origin []int) []int {
    return origin[len(origin)-2:]
}

// copy,占用空间小,推荐使用:
func GetLastBySlice(origin []int) []int {
    result := make([]int, 2]
    copy(result, origin[len(origin)-2:])
    return result
}
优化建议 - Map:

同样的,map 也建议预分配内存来避免扩容机制的时间开销。

  • 不断向 map 中添加元素会触发 map 的扩容。
  • 提前分配好空间可以减少内存拷贝和 Rehash 的消耗。
  • 建议根据实际需求提前预估好需要的空间。
func GetLastBySlice(origin []int) []int {
    data := make(map[int]int, size)
    for i := 0; i < size; i++ {
        data[i] = 666
    }
}
优化建议 - 字符串处理:

和 Java 语言类似,Golang 中直接使用 + 拼接字符串是一种十分低效的方式,因为字符串是不可变类型,使用 + 每次都会重新分配内存,推荐使用 strings.Builderbytes.Buffer 操作字符串(strings.Builder 效率要更高一些)。

// 使用加号拼接字符串,不推荐
func Plus(n int, str string) string {
    s := ""
    for i := 0; i < n; i++ {
        s += str
    }
    return s
}

// 使用strings.Builder拼接字符串
func StrBuilder(n int, str string) string {
    var builder strings.Builder
    for i := 0; i < n; i++ {
        builder.WriteString(str)
    }
    return builder.String()
}

此外 strings.Builderbytes.Buffer 都支持使用 Grow() 函数预分配内存,在可以预知长度的情况下提前分配内存,可以提高字符串拼接的效率。

// strings.Builder:
var builder strings.Builder
builder.Grow(n * len(str))

// bytes.Buffer:
buf := new(bytes.Buffer)
buf.Grow(n * len(str))
优化建议 - 空结构体:

使用空结构体 struct{} 可以节省内存。

  • 空结构体实例不占据任何的内存空间。
  • 可作为各种场景下的占位符使用。
    • 节省资源。
    • 空结构体本身具备很强的语义,即这里不需要任何值,仅作为占位符。

比如在实际的开发中,我们经常会使用到 Set 这种数据结构,然而 Golang 本身并不支持 Set,我们可以考虑用 map 来代替。换句话说我们只用到 map 的键,而不用它的值,那么值可以用 struct{} 类型占位。

func EmptyStructMap(n int) {
    m := make(map[int]struct{})
    
    for i := 0; i < n; i++ {
        m[i] = struct{}{}
    }
}
优化建议 - atomic 包:

atomic 包主要用在多线程编程,相比于加锁的方式来保证并发安全,atomic 包效率更高。

  • 锁的实现是通过操作系统来实现,属于系统调用。
  • atomic 操作是通过硬件实现,效率比锁高。
  • sync.Mutex 应该用来保护一段逻辑,不仅仅用于保护一个变量,因此成本比较大。
  • 对于非数值操作,可以使用 atomic.Value,能承载一个 interface{}
type atomicCounter struct {
    i int32
}

func AtomicAddOne(c *atomicCounter) {
    atomic.AddInt32(&c.i, 1)
}

性能优化分析工具

pprof 是用于可视化和分析性能、分析数据的工具,帮助我们了解应用在什么地方耗费了多少 CPU、内存等。

image.png

pprof - 排查实战

GitHub 上提供了 pprof 工具的实验项目,通过对项目的性能分析实战,帮助我们了解 pprof 工具的使用流程,便于我们今后分析一些更为复杂的程序。

🎈前置准备:

下载 GitHub 上的项目代码,该项目提前买入了一些炸弹代码,产生可观测的性能问题。

🚀项目传送门: wolfogre/go-pprof-practice: go pprof practice. (github.com)

🚀实战手册传送门: golang pprof 实战 | Wolfogre’s Blog

🎈pprof 命令总结:

pprof 实战手册中给出了详细的项目实验步骤,这里就不再赘述了,主要记录以下课程中主讲老师给出的一些 pprof 的常用命令。

  • 启动项目以后可以使用浏览器访问 localhost:6060/debug/pprof 查看指标,在项目的 main.go 文件中指定了 pprof 的访问端口:
    // 代码片段...
    go func() { 
    // 启动一个 http server,注意 pprof 相关的 handler 已经自动注册过了 
    if err := http.ListenAndServe(":6060", nil); err != nil {
        log.Fatal(err) } os.Exit(0) 
    }()
    // ...
    
  • pprof 提供了命令来在终端中获取采样数据:
    go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
    
    随后可以使用接下来的各种命令来有针对性的获取特定资源的使用情况。
  • topN 命令查看占用资源最多的函数,会显示以下几点数据:
    • flat - 当前函数本身的执行耗时;
    • flat% - flat 占 CPU 总时间的比例;
    • sum% - 上面每一行的 flat% 总和;
    • cum - 指当前函数本身加上其调用函数的总耗时;
    • cum% - cum 占 CPU 总时间的比例。
  • list 命令用来根据指定的正则表达式查找代码行。
  • web 命令用来将调用关系可视化展示。
  • pprof 的浏览器面板会将所有指标以平铺的方式展现,看起来并不直观,在 pprof 命令中加上一个可选项可以以可视化的方式展现监控数据。
    • 查看堆内存:
      go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"
      
    • 查看 goroutine:
      go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"
      

性能调优

本次课程性能调优部分主通过实际业务服务性能优化的案例介绍了性能调优的思路,可以从三方面入手:业务服务优化、基础库优化、Go 语言优化。

业务服务优化

image.png

🎯基本概念:

  • 服务:能单独部署。能承载一定功能的程序。
  • 依赖:Service A 的功能实现依赖 Service B 的响应结果,称 Service A 依赖 Service B。
  • 调用链路:能支持一个接口请求的相关服务集合及其相互之间的依赖关系。
  • 基础库:公共的工具包、中间件。

☕流程:

  1. 建立服务性能评估手段。
  2. 分析性能数据,定位性能瓶颈。
  3. 重点优化项改造。
  4. 优化效果验证。
基础库优化

☕流程:

  1. 分析基础库核心逻辑和性能瓶颈。
    • 完善设计改造方案;
    • 数据按需获取;
    • 数据序列化协议优化。
  2. 内部测压验证。
  3. 推广业务服务落地验证。
Go 语言优化

🍬编译器 & 运行时优化:

  • 优化内存分配策略。
  • 优化代码编译流程,生成更高效的程序。
  • 内部测压验证。
  • 推广业务服务落地验证。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/420390.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

叶酸聚乙二醇羟基FA-PEG-OH;一文带你了解高分子试剂OH-PEG-Folate

FA-PEG-OH&#xff0c;叶酸-聚乙二醇-羟基 中文名称&#xff1a;叶酸聚乙二醇羟基 英文名称&#xff1a;FA-PEG-OH HO-PEG-FA Folate-PEG-OH 性状&#xff1a;黄色液体或固体&#xff0c;取决于分子量 溶剂&#xff1a;溶于水&#xff0c;DMSO、DMF等常规性有机溶剂 活性基…

城市地下综合管廊安全运营与智慧管控的分层架构研究

安科瑞 李亚俊 1、引言 1833年&#xff0c;市政管线综合管廊在巴黎城市地下建成至今&#xff0c;经过百年来的探索、研究、改良和实践&#xff0c;法国、英国、德国、俄罗斯、日本、美国等发达国家的管廊规划建设与安全运维体系已经日臻完善&#xff0c;截止目前&#xff0c;…

《花雕学AI》17:关注提示工程—本世纪最重要的技能可能就是与AI人工智能对话

本文目录与主要结构 引言&#xff1a;介绍提示工程的概念和背景&#xff0c;说明为什么它是本世纪最重要的技能之一。 正文&#xff1a; 一、提示工程的基本原理和方法&#xff1a;介绍什么是提示、如何设计和优化提示、如何使用提示与语言模型进行交互。 二、提示工程的应用和…

Direct3D 12——混合——混合

混合运算 typedef enum D3D12_BLEND_OP {D3D12_BLEND_OP_ADD 1, //添加源 1 和源 2。D3D12_BLEND_OP_SUBTRACT 2,//从源 2 中减去源 1。D3D12_BLEND_OP_REV_SUBTRACT 3,//从源 1 中减去源 2。D3D12_BLEND_OP_MIN 4,//查找源 1 和源 2 的最小值。D3D12_BLEND_OP_MAX 5//查…

【云原生|Docker】13-Docker-compose详解

【云原生Docker】13-Docker-compose详解 文章目录【云原生Docker】13-Docker-compose详解前言docker-compose简介docker-compose安装docker-compose基本示例Docker Compose常用命令说明Docker Compose文件详解versionserviceimagebuildcommandlinksexternal_linksportsexposeen…

Win11的两个实用技巧系列之找不到wifi网络的解决方法、双系统开机选择系统方法

Win11装了VMware后找不到wifi网络的解决方法 有用户在电脑上安装了VMware虚拟机来使用的时候&#xff0c;发现虚拟机中无法进行无线网络的连接了&#xff0c;本文就为大家带来了详细的解决方法&#xff0c;一起看看吧 Win11装了VMware后找不到wifi网络的解决方法教学分享。有用…

助力信创国产化,Solon v2.2.9 发布

Solon 是一个高效的 Java 应用开发框架&#xff1a;更快、更小、更简单。它不是 Spring、没有使用 Servlet、JavaEE 接口&#xff0c;是一个有自己接口标准的开放生态。可以为应用软件国产化提供支持&#xff0c;助力信创建设。 150来个生态插件&#xff0c;覆盖各种不同的应用…

【SSM框架】Spring更简单的存储对象

Spring更简单的存储对象将Bean对象简单存储到Spring容器中使用五大类注解来存储Bean对象使⽤ Controller 存储 bean 对象使⽤ Service 存储 bean 对象使⽤ Repository 存储 bean 对象使⽤ Component 存储 bean 对象使⽤ Configuration为什么要有五大类注解五大类注解的关系五大…

【C++初阶】类与对象(一)

文章目录一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装1 、访问限定符2.封装五、类的作用域六、类的实例化七、类对象模型1.探究存储方式2.结构体内存对齐规则八、this指针1、this指针的引出2.this指针的特性八、C语言和C实现Stack的对比总…

漏洞挖掘小技巧(一)

Swagger UI反射XSS Swagger UI是目前最流行的RestFul接口API文档和测试工具。 首先写一个 json的 XSS 负载 https://gist.githubusercontent.com/ramkrivas/c47c4a49bea5f3ff99a9e6229298a6ba/raw/e2e610ea302541a37604c7df8bcaebdcb109b3ba/xsstest.json回到Swagger UI&…

轮廓特征属性及应用

轮廓特征属性及应用 1)凸包 凸包(Convex Hull)是一个计算机几何图形学中的概念, 简单来说, 给定二维平面点集, 凸包就是能够将最外层的点连接起来构成的凸多边形, 它能够包含点集中所有的点。物体的凸包检测常应用在物体识别、手势识别及边界检测等领域。 寻找凸包—cv2.co…

数字中国背景下,企业加大数据决策投入,零代码+商业智能成为新选型 | 爱分析洞察

自“十四五”规划将“加快数字化发展&#xff0c;建设数字中国”单独成篇&#xff0c;从国家战略层面明确了数字化转型的重要性&#xff0c;国家层面有关支持数字经济发展的顶层设计与配套政策此后相继出台。2023年3月1日&#xff0c;中共中央、国务院印发了《数字中国建设整体…

一、基础算法7:位运算 模板题+算法模板(二进制中1的个数)

文章目录算法模板求n的第k位数字返回n的最后一位1求一个数x的二进制中1的个数模板模板题二进制中1的个数原题链接题目题解补充输出一个数n的补码x的二进制并按位输出关于原码&#xff0c;反码&#xff0c;补码算法模板 求n的第k位数字 n >> k & 1返回n的最后一位1 …

iOS-关联对象(Objective-C)

关联对象关联对象的应用分类中的 property使用关联对象关联对象的实现objc_setAssociatedObjectAssociationsManager如何存储 ObjcAssociationnew_value ! nilnew_value nilsetHasAssociatedObjects()objc_getAssociatedObjectobjc_removeAssociatedObjects写到最后关于应用关…

ESP32设备驱动-VEML6040颜色传感器驱动

VEML6040颜色传感器驱动 文章目录 VEML6040颜色传感器驱动1、VEML6040介绍2、硬件准备3、软件准备4、驱动实现1、VEML6040介绍 VEML6040 颜色传感器可感应红光、绿光、蓝光和白光,并使用 CMOS 工艺将光电二极管、放大器和模拟/数字电路集成到单个芯片中。 通过应用颜色传感器…

【Spring】— Spring基础学习

Spring基础学习一、Spring概述1.什么是Spring2.Spring的下载及目录结构2.1 Spring框架包2.2 第三方依赖包二、控制反转&#xff08;IoC&#xff09;与依赖注入&#xff08;DI&#xff09;1.什么是控制反转&#xff08;IoC&#xff09;2.什么是依赖注入&#xff08;DI&#xff0…

u-view2.0 引入iconfont(阿里字体图标库)详细步骤!

uView已通过大量的实践中&#xff0c;收集了用户最有可能需要用到的图标&#xff0c;见Icon 图标&#xff0c;但我们也相信&#xff0c;它肯定无法覆盖所有的场景和需求。 用户也可以使用标签的方式&#xff0c;自行引入字体图标&#xff0c;为何要通过扩展的方式集成呢&#…

树莓派 ROS 学习(二)小乌龟 turtlesim

添加链接描述 目录 创建catkin工作空间 小乌龟turtlesim节点 启动节点 turtlesim节点 turtlesim 话题与消息 查看话题的消息类型 查看消息类型的具体内容 查看话题的值 改变背景颜色 获取参数列表 获取参数值 修改颜色 控制乌龟运动 通过发布话题控制乌龟运动 通过键盘控制乌龟…

力扣sql中等篇练习(一)

力扣sql中等篇练习(一) 1 第二高的薪水 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 SELECT IFNULL((SELECT distinct salary SecondHighestSalaryFROM EmployeeORDER BY salary desclimit 1,1),null ) SecondHighestSalary1.3 运行截图 2 分数排…

【C++】命名空间,缺省参数,函数重载,引用,内联函数,auto 关键字

目录1. 命名空间2. 输入输出3. 缺省参数4. 函数重载为什么C支持函数重载&#xff1f;5. 引用5.1 引用作函数参数&#xff08;输出型参数&#xff09;5.2 作函数的返回值关于函数的返回值&#xff1a;5.3 引用权限关于类型转换&#xff1a;5.4 引用和指针6. 内联函数6.1 C推荐的…