15分钟学 Go 第 35 天:Go的性能调优 (7000字详细教程)
第35天:Go的性能调优
目标:理解Go语言中基本的性能优化,学习如何分析和提高Go程序的执行效率。
一、性能调优概述
性能调优是软件开发中的一个重要环节,它可以确保程序在资源有限的环境下高效运行。Go语言天生具备高效的性能表现,但即使如此,也有很多细节可以通过优化进一步提升程序的执行速度和资源使用效率。
在本节中,我们将重点介绍以下几种性能调优的策略:
- 内存优化:减少内存分配,优化GC(垃圾回收)。
 - CPU优化:通过减少不必要的计算、并发调度和锁竞争提升CPU效率。
 - I/O优化:提高文件、网络等I/O操作的性能。
 
二、性能分析工具
在进行性能优化之前,首先需要通过分析工具发现性能瓶颈。Go 语言提供了一些内置工具,帮助开发者分析和优化程序的性能。
1. pprof 性能分析工具
 
Go 标准库中的 pprof 包可以生成并分析 CPU、内存等的性能数据。你可以通过命令行或 Web 界面分析这些数据。
使用 pprof 的基本步骤:
 
-  
在代码中引入
pprofimport _ "net/http/pprof" -  
启动性能分析
在代码运行时,将性能数据暴露在 HTTP 服务器上:go func() {log.Println(http.ListenAndServe("localhost:6060", nil)) }() -  
访问性能数据
通过localhost:6060/debug/pprof/获取性能数据,包括 CPU 使用、内存分配等。 -  
生成性能报告
使用go tool pprof来生成性能分析报告。 
常见 pprof URL:
 
localhost:6060/debug/pprof/goroutine:查看当前 goroutine 数量。localhost:6060/debug/pprof/heap:查看内存堆使用情况。localhost:6060/debug/pprof/profile?seconds=30:生成 CPU 分析报告,记录 30 秒的数据。
2. trace 跟踪工具
 
Go trace 工具用于跟踪程序的执行时间、goroutine 的创建和调度、系统调用等情况,有助于调优并发和 I/O 性能。
使用 trace 的步骤:
 
-  
启动跟踪并生成日志文件:
go test -trace trace.out -  
分析
trace文件:go tool trace trace.out 
三、内存优化
1. 减少内存分配
内存分配是影响性能的一个主要因素。频繁的小内存分配会导致 GC(垃圾回收)压力增加,从而影响程序性能。因此,减少不必要的内存分配是提高性能的关键之一。
示例:优化内存分配
不优化的代码:
package mainfunc createSlice() []int {s := make([]int, 0)for i := 0; i < 100000; i++ {s = append(s, i)}return s
}func main() {_ = createSlice()
}
 
上面的代码每次 append 都可能导致重新分配内存。可以通过预先指定容量减少多次分配内存的操作。
优化后的代码:
package mainfunc createSlice() []int {s := make([]int, 0, 100000) // 预先分配足够的容量for i := 0; i < 100000; i++ {s = append(s, i)}return s
}func main() {_ = createSlice()
}
 
2. 使用对象池 (sync.Pool)
 
Go 语言的 sync.Pool 是一个对象池,用于缓存和重用临时对象,从而减少内存分配和GC压力。适合用于短期生命周期对象的优化。
示例:使用 sync.Pool
 
package mainimport ("fmt""sync"
)func main() {var pool = sync.Pool{New: func() interface{} {return new(int) // 创建一个新的对象},}// 从对象池获取对象obj := pool.Get().(*int)*obj = 100fmt.Println(*obj)// 将对象放回池中pool.Put(obj)// 重新从池中获取对象newObj := pool.Get().(*int)fmt.Println(*newObj) // 对象被重用
}
 
3. 减少逃逸分析
Go 编译器使用逃逸分析来决定变量是分配在栈上还是堆上。分配在堆上的对象会增加 GC 压力,因此减少逃逸的对象是优化的重点。
示例:逃逸分析
package mainimport "fmt"func main() {x := 10fmt.Println(&x) // x 逃逸到堆上
}
 
优化后,尽量减少变量的指针传递:
package mainimport "fmt"func main() {x := 10fmt.Println(x) // x 保留在栈上
}
 
四、CPU优化
1. 并发优化
Go 语言的并发模型基于 goroutine 和通道。虽然 goroutine 是轻量级的,但它们的数量和调度方式会直接影响程序的 CPU 使用情况。
示例:不合理的并发
package mainimport "sync"func main() {var wg sync.WaitGroupfor i := 0; i < 100000; i++ {wg.Add(1)go func() {defer wg.Done()}()}wg.Wait()
}
 
这种写法可能创建大量的 goroutine,造成不必要的上下文切换。可以通过限制 goroutine 的数量来优化。
示例:优化并发
package mainimport ("sync"
)func main() {var wg sync.WaitGrouppoolSize := 100 // 控制并发数量sem := make(chan struct{}, poolSize)for i := 0; i < 100000; i++ {wg.Add(1)sem <- struct{}{}go func() {defer wg.Done()<-sem}()}wg.Wait()
}
 
2. 减少锁竞争
当多个 goroutine 同时访问共享资源时,锁竞争会导致性能下降。应尽量减少锁的使用或缩小锁的粒度。
示例:锁竞争优化
package mainimport ("sync""time"
)var mu sync.Mutexfunc criticalSection() {mu.Lock()time.Sleep(1 * time.Second)mu.Unlock()
}func main() {for i := 0; i < 10; i++ {go criticalSection()}time.Sleep(2 * time.Second)
}
 
优化策略:缩小锁的作用范围或使用 sync.RWMutex 读写锁。
package mainimport ("sync""time"
)var mu sync.RWMutexfunc readSection() {mu.RLock()time.Sleep(1 * time.Second)mu.RUnlock()
}func writeSection() {mu.Lock()time.Sleep(1 * time.Second)mu.Unlock()
}func main() {for i := 0; i < 10; i++ {go readSection()}time.Sleep(2 * time.Second)
}
 
五、I/O优化
1. 缓存读写操作
频繁的 I/O 操作(文件读写、网络请求等)是性能瓶颈之一。通过缓存减少 I/O 的频率,可以显著提高程序性能。
示例:优化文件读取
不使用缓存的文件读取:
package mainimport ("io/ioutil""log"
)func main() {content, err := ioutil.ReadFile("largefile.txt")if err != nil {log.Fatal(err)}log.Println(len(content))
}
 
优化后的代码,使用 bufio 缓存读写:
package mainimport ("bufio""log""os"
)func main() {file, err := os.Open("largefile.txt")if err != nil {log.Fatal(err)}defer file.Close()reader := bufio.NewReader(file)buffer := make([]byte, 1024)for {_, err := reader.Read(buffer)if err != nil {break}}
}
 
2. 异步I/O
在进行网络请求、数据库操作等可能涉及延迟的I/O操作时,使用异步I/O可以避免阻塞主线程,提升系统的吞吐量。Go语言的goroutine天生适合处理这种并发场景,通过使用goroutine和channel的组合,可以实现高效的异步I/O处理。
示例:同步I/O vs 异步I/O
同步I/O(阻塞):
package mainimport ("fmt""net/http""time"
)func fetchData(url string) {start := time.Now()resp, err := http.Get(url)if err != nil {fmt.Println("Error:", err)return}defer resp.Body.Close()fmt.Println("Fetched data from:", url, "in", time.Since(start))
}func main() {fetchData("https://example.com")fetchData("https://golang.org")
}
 
在同步I/O操作中,第二次请求必须等到第一次请求结束后才能开始。这样可能会延长程序的总运行时间。
异步I/O(非阻塞):
package mainimport ("fmt""net/http""time"
)func fetchData(url string, ch chan<- string) {start := time.Now()resp, err := http.Get(url)if err != nil {ch <- fmt.Sprintf("Error fetching %s: %v", url, err)return}defer resp.Body.Close()ch <- fmt.Sprintf("Fetched data from %s in %v", url, time.Since(start))
}func main() {ch := make(chan string)go fetchData("https://example.com", ch)go fetchData("https://golang.org", ch)fmt.Println(<-ch)fmt.Println(<-ch)
}
 
在异步I/O版本中,我们使用goroutine并发地处理多个请求,从而显著减少了总执行时间。
六、性能优化的流程图
下图展示了Go性能优化的步骤,从初步的性能分析,到针对性的优化策略:
+----------------------------------+
|       性能分析(CPU/内存/I/O)      |
|    使用pprof或trace工具进行分析     |
+----------------------------------+|v
+----------------------------------+
|        找到性能瓶颈(热点部分)      |
|    分析代码中的资源消耗点            |
+----------------------------------+|v
+----------------------------------+
|     选择合适的优化策略(CPU/内存/I/O)|
|  比如:减少内存分配、优化并发模型      |
+----------------------------------+|v
+----------------------------------+
|       进行代码优化                 |
|  重构代码、减少锁竞争或I/O延迟       |
+----------------------------------+|v
+----------------------------------+
|       重新测试程序性能              |
|   确保优化后的性能有提升             |
+----------------------------------+
 
通过不断迭代和分析,开发者可以逐步提高程序的性能。
七、常见的性能优化策略对比
下表总结了不同场景下的常用性能优化策略,以及它们适用的情况。
| 优化策略 | 场景 | 优点 | 缺点 | 
|---|---|---|---|
| 预先分配内存 | 大量动态增长的slice | 减少内存分配次数,降低GC压力 | 需要准确估计容量,可能会导致内存浪费 | 
使用 sync.Pool | 临时对象的频繁创建 | 重用对象,减少垃圾回收的开销 | 适用范围有限,适合短期对象 | 
| 并发控制 | 高并发场景 | 控制goroutine数量,减少上下文切换 | 需要手动设计并发模型 | 
| 缓存I/O | 大量文件或网络请求 | 减少I/O次数,提升吞吐量 | 增加了缓存管理的复杂度 | 
| 异步I/O | 网络、数据库操作 | 非阻塞处理,提升响应速度 | 需要处理异步回调的复杂性 | 
| 缩小锁的粒度 | 高锁竞争场景 | 减少锁的持有时间,降低锁竞争 | 可能会导致更多锁,增加代码复杂度 | 
| 减少指针逃逸 | 大量堆内存分配 | 降低GC压力,提升内存访问效率 | 需要手动调整变量生命周期 | 
八、性能优化中的常见陷阱
在性能优化的过程中,有几个常见的陷阱需要避免:
-  
过度优化
不要为优化而优化。在性能调优前,先确保程序的正确性和可读性,只有当性能瓶颈确实对系统造成影响时,才进行优化。微小的性能提升往往并不值得复杂化代码。 -  
忽视分析工具
使用工具进行性能分析是至关重要的。不要凭借直觉来判断瓶颈位置,借助pprof或trace等工具来验证性能问题。 -  
忽略GC和内存泄漏
Go 的垃圾回收机制很强大,但如果不加控制,频繁的内存分配和回收可能会影响程序的性能。通过go tool pprof分析GC的开销,避免内存泄漏和过多的对象逃逸到堆上。 -  
并发过度
虽然Go的goroutine是轻量级的,但并不意味着可以肆意创建成千上万的goroutine。在并发场景中,合理控制goroutine的数量,防止过多的上下文切换带来性能问题。 
九、代码优化示例
我们通过一个综合示例,展示如何从性能分析到优化实现。
示例:简单HTTP服务器的性能优化
初始版本:
package mainimport ("fmt""net/http"
)func handler(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}func main() {http.HandleFunc("/", handler)http.ListenAndServe(":8080", nil)
}
 
这是一个简单的HTTP服务器,每次请求都会处理并返回一个Hello消息。在高并发场景下,它的性能可能表现不佳。
性能优化步骤:
-  
性能分析:
通过pprof工具,发现瓶颈主要在于请求处理的性能。 -  
并发优化:
通过引入sync.Pool缓存响应对象,减少每次请求的内存分配开销。 -  
I/O优化:
使用bufio进行缓冲写入,减少I/O操作次数。 
优化后的版本:
package mainimport ("bufio""fmt""net/http""sync"
)var bufPool = sync.Pool{New: func() interface{} {return bufio.NewWriter(nil)},
}func handler(w http.ResponseWriter, r *http.Request) {bw := bufPool.Get().(*bufio.Writer)bw.Reset(w)fmt.Fprintf(bw, "Hello, %s!", r.URL.Path[1:])bw.Flush()bufPool.Put(bw)
}func main() {http.HandleFunc("/", handler)http.ListenAndServe(":8080", nil)
}
 
通过这几步优化,HTTP服务器的内存分配减少了,I/O操作得到了优化,从而提升了系统的整体吞吐量。
十、总结
通过今天的学习,你应该了解了Go语言中基本的性能优化策略。性能调优不仅是为了提升程序的运行速度,更是为了合理分配系统资源。记住,在进行优化前,先使用分析工具找到性能瓶颈,再针对性地进行优化。同时,务必保持代码的可读性,避免过度优化。
关键点回顾:
- 使用 
pprof、trace等工具分析性能瓶颈。 - 通过减少内存分配、优化并发和I/O操作来提升性能。
 - 保持代码简单可维护,避免过度优化。
 
怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!
相关文章:
15分钟学 Go 第 35 天:Go的性能调优 (7000字详细教程)
第35天:Go的性能调优 目标:理解Go语言中基本的性能优化,学习如何分析和提高Go程序的执行效率。 一、性能调优概述 性能调优是软件开发中的一个重要环节,它可以确保程序在资源有限的环境下高效运行。Go语言天生具备高效的性能表现…...
6、显卡品牌分类介绍:技嘉 - 计算机硬件品牌系列文章
技嘉科技是一家以主板、显卡在业界缔造无以撼动的地位的科技公司,其核心理念是「技术创新、质量稳定」的高标准。技嘉专注于关键技术研发,其经营范围涵盖家用、商用、电竞等多元科技领域。通过应用突破性的专利技术,技…...
Redis数据类型——针对实习面试
目录 Redis数据类型Redis常用的数据类型有哪些?String类型可以用于哪些场景?Set类型可以用于哪些场景?Bitmaps类型可以用于哪些场景?HyperLogLog类型可以用于哪些场景?Hash类型与Set类型有什么区别?Hash类型…...
roberta融合模型创新中文新闻文本标题分类
项目源码获取方式见文章末尾! 600多个深度学习项目资料,快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于CNN-RNN的影像报告生成】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模型实现…...
《密码系统设计》实验二 4-6学时
文章目录 《密码系统设计》实验实验项目实验二 密码算法实现4-6 学时实践要求(30 分)1. 定义宏2. 使用特定的源文件3. 编译MIRACL库4. 配置KCM和Comba方法5. 编译和运行MEX工具6. 使用config.c工具总结1. 准备环境2. 下载和解压MIRACL库3. 定义宏4. 使用…...
Zypher Network:全栈式 Web3 游戏引擎,服务器抽象叙事的引领者
近期,《黑神话:悟空》的爆火不仅让 AAA 游戏重回焦点,也引发了玩家与开发者的热议。Web2 游戏的持续成功导致部分 Web3 玩家们的倒戈,对比之下 Web3 游戏存在生命周期短且商业模式难以明确的问题,尤其在当前加密市场环…...
2025生物发酵展(济南)为生物制造产业注入新活力共谱行业新篇章
2025第十四届国际生物发酵展将于3月3-5日济南盛大举办!产业链逐步完整,展会面积再创历史新高,展览面积较上届增涨至60000平方米,专业观众40000,品牌展商800,同期活动会议增加至50场,展会同期将举…...
git入门教程14:Git与其他工具的集成
一、Git与代码托管平台的集成 GitHub 集成方式: 在GitHub上创建或克隆仓库。在本地使用Git命令进行代码提交和推送(如git push)。GitHub提供Web界面进行代码浏览、协作和持续集成配置。 特点: 支持Pull Request,便于代…...
在Zetero中调用腾讯云API的输入密钥的问题
也是使用了Translate插件了,但是需要调用腾讯云翻译,一直没成功。 第一步就是,按照这上面方法做:百度、阿里、腾讯、有道各平台翻译API申请教程 之后就是:Zotero PDF translat翻译:申请腾讯翻译接口 主要是…...
【AD】1-8 AD24软件工程创建
1.点击文件,新建项目 2.如图进行设置工程名称和文件路径 3.创建原理图库及原理图,并保存 4.新建PCB库及PCB,并保存 5.单击右键工程保存 注意:先新建工程,在新建文件...
RT-Thread学习
文章目录 前言一、rtt的启动流程二、移植工作总结 前言 RT-Thread学习,这里记录对bsp的移植 一、rtt的启动流程 RT-Thread 支持多种平台和多种编译器,而 rtthread_startup() 函数是 RT-Thread 规定的统一启动入口。一般执行顺序是:系统先从…...
20241102在荣品PRO-RK3566开发板使用荣品预编译的buildroot通过iperf2测试AP6256的WIFI网速
20241102在荣品PRO-RK3566开发板使用荣品预编译的buildroot通过iperf2测试AP6256的WIFI网速 2024/11/2 14:18 客户端:荣耀手机HONOR 70【iPerf2 for Android】 服务器端:荣品PRO-RK3566开发板 预编译固件:update-pro-rk3566-buildroot-hdmi-2…...
网络模型——二层转发原理
网课地址:网络模型_二层转发原理(三)_哔哩哔哩_bilibili 一、路由交换 网络:用来信息通信,信息共享的平台。 网络节点(交换机,路由器,防火墙,AP)介质&#…...
【编程技巧】C++如何使用std::map管理std::function函数指针
一、问题背景 开发过程中遇到了需要根据const字符串调用不同函数的要求。在开发过程中为了快速实现功能,实际使用了if else等判断实现了不同函数的调用,徒增了不少代码行数。 明知道可以采用map管理函数指针,但是没有具体实现过,…...
导航栏小案例
实现类似于这样的效果 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>导航栏</title><style>*{margin: 0;padding: 0;}.div1{width: 100%;height: 60px;/* border: 1px solid blue; */background-color:rgb(…...
MyBatis一文入门精通,面试题(含答案)
一、MyBatis详细介绍 MyBatis 是一个流行的 Java 持久层框架,主要用于简化 SQL 数据库操作。它的设计初衷是通过 XML 或注解的方式配置和执行 SQL 语句,使得数据库操作更加灵活、方便和高效。相比于传统的 JDBC,MyBatis 提供了一些关键优势&…...
Ubuntu18.04服务器非root用户在虚拟环境下的python版本设定
最近需要跑一个python3.9.16版本的代码,Ubuntu18.04服务器上是上次博客中已经定死的python3.8.0版本 需要创建一个虚拟环境,并且在虚拟环境中配置python3.9.16版本 只需要创建一个虚拟环境 conda create -n yyy python3.9.16yyy是你的虚拟环境名字 创建…...
CodeS:构建用于文本到 SQL 的开源语言模型
发布于:2024 年 10 月 29 日 #RAG #Text2 SQL #NL2 SQL 语言模型在将自然语言问题转换为 SQL 查询(文本到 SQL )的任务中显示出良好的性能。然而,大多数最先进的 (SOTA) 方法都依赖于强大但闭源的大型语言…...
HTML 基础概念:什么是 HTML ? HTML 的构成 与 HTML 基本文档结构
文章目录 什么是 HTML ?HTML 的构成 ?什么是 HTML 元素?HTML 元素的组成部分HTML 元素的特点 HTML 基本文档结构如何打开新建的 HTML 文件代码查看 什么是 HTML ? HTML(超文本标记语言,HyperText Markup L…...
18 Docker容器集群网络架构:一、etcd 概述
文章目录 Docker容器集群网络架构:一、etcd概述1.1 etcd 的基本概念和特点1.1.1 定义1.1.2 特点1.2 etcd 在 Docker 集群网络中的作用1.3 etcd 集群的架构和原理1.3.1 架构1.3.2 原理Docker容器集群网络架构:一、etcd概述 etcd是一个高可用的分布式键值存储系统,它主要用于…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...
Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
【技巧】dify前端源代码修改第一弹-增加tab页
回到目录 【技巧】dify前端源代码修改第一弹-增加tab页 尝试修改dify的前端源代码,在知识库增加一个tab页"HELLO WORLD",完成后的效果如下 [gif01] 1. 前端代码进入调试模式 参考 【部署】win10的wsl环境下启动dify的web前端服务 启动调试…...
Xcode 16.2 版本 pod init 报错
Xcode 版本升级到 16.2 后,项目执行 pod init 报错; ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchron…...
C#学习12——预处理
一、预处理指令: 解释:是在编译前由预处理器执行的命令,用于控制编译过程。这些命令以 # 开头,每行只能有一个预处理指令,且不能包含在方法或类中。 个人理解:就是游戏里面的备战阶段(不同对局…...
