首页 > 编程语言 >go tool trace 跟踪执行过程,程序优化过程

go tool trace 跟踪执行过程,程序优化过程

时间:2024-01-22 13:11:25浏览次数:24  
标签:wg trace tool 并发 func go Go 优化

Go并发优化的9大技巧,效果立竿见影

原创 Go先锋 Go先锋 2023-11-10 08:02 发表于广东 听全文

Go 先锋

读完需要

8分钟

速读仅需 3 分钟

   

概述

Go 语言 以其在并发编程方面的优势而闻名,但合理利用各种优化技巧可以进一步提升 Go 程序的并发性能。

本文将介绍在 CPU 密集型 和 IO 密集型 场景下优化 Go 并发程序的常见方法。

主要内容包括

  • 充分利用多核 CPU

  • 减少调度和上下文切换

  • 控制内存占用

  • 选择合适的数据结构

  • 分析性能瓶颈

 

   

一、优化 CPU 密集场景

对于计算密集型任务, 可通过以下方法利用多核 CPU

1. 设置 GOMAXPROCS

func init() {  runtime.GOMAXPROCS(runtime.NumCPU()) }

2. 分解问题,并行计算

// 并发求和func sum(s []int) int {  var wg sync.WaitGroup  ch := make(chan int)  for i := range s {    wg.Add(1)    go func(slice []int) {      ch <- sum(slice)      wg.Done()     }(s[i:])  }
go func() { wg.Wait() close(ch) }()
var total int for c := range ch { total += c } return total}

3. 避免不必要的 Goroutine 上下文切换

合理控制 Goroutine 数量,避免过多的 Goroutine 导致调度器负载过重。

 

   

二、优化 IO 密集场景

对于 IO 密集型 任务, 可通过以下方法并发提高吞吐量

1. 多路复用 IO 操作

func readFromFiles(filenames []string) {   var wg sync.WaitGroup   fileCh := make(chan File)   for _, f := range filenames {      wg.Add(1)      go func(f string) {         fileCh <- readFile(f)          wg.Done()      }(f)   }
go func() { wg.Wait() close(fileCh) }()
for r := range fileCh { process(r) }}

2.异步处理 IO 请求

使用 bufio.Scanner 等可以异步读取数据。

3.控制并发数

避免过多的 Goroutine 阻塞导致资源耗尽。

 

   

三、减少内存使用详解

优化内存使用非常重要,主要可从以下几个方面入手

1. 使用 sync.Pool 对象重用

// 重用解析对象  var parserPool = sync.Pool{  New: func() interface{} {    return &Parser{}   },}
func parse(data []byte) { p := parserPool.Get().(*Parser) // 使用p解析 parserPool.Put(p) // 重用}

2. 优化 Goroutine 栈大小

降低栈空间,可以支持更多 Goroutine

g := runtime.NewGoroutineWithStackSize(512 * 1024)

3. 复用缓冲区

重用字节缓冲区,减少内存分配

var buffPool = sync.Pool{  New: func() interface{} {    return make([]byte, 1024)  },} 
func read() { b := buffPool.Get().([]byte)  // 复用buff buffPool.Put(b)}

 

   

四、选择并发数据结构

  1. 读写频繁时优先使用 RWMutex

     

  2. 需要线程安全可以选择 sync.Map

     

  3. 不需要锁的场景可使用原子操作

     

  4. channel 并发安全但需要合理 buffer 数

     

  5. 无锁数据结构如环形队列

例如

// 环形队列 type ringBuffer struct {  buf []interface{}  count uint64   head, tail uint32}
func (rb *ringBuffer) Push(val interface{}) { if rb.Full() { rb.head++ // 头部推进 } rb.buf[rb.tail] = val rb.tail++ // 尾部推进 }

 

   

五、分析并发程序效率

可用以下工具来分析 Go 并发程序的效率

1. GODEBUG=schedtrace 追踪调度过程

import "runtime"
// 启用调度跟踪runtime.GODEBUG = "schedtrace=1000"
func main() { // 执行程序 // 分析log结果}

2. runtime/pprof 做性能分析

import "runtime/pprof"
func main() { pprof.StartCPUProfile(w io.Writer) defer pprof.StopCPUProfile()
// 程序代码
pprof.WriteHeapProfile(w io.Writer)}

3. go tool trace 跟踪执行过程

go test -trace=trace.out 
go tool trace trace.out# 分析

用这些工具可以直观地分析并发程序的效率问题。

 

   

六、爬虫程序优化案例

对一个爬虫程序进行并发优化

var urlPool = make(chan []string)
func main() { // 使用无锁队列 urlPool = make(chan []string, 10000)
var wg sync.WaitGroup // 限制最大并发数 maxG := 100 wg.Add(maxG) for i := 0; i < maxG; i++ { go func() { crawl() wg.Done() }() } // 分发URL go distributeUrls() wg.Wait() }
func crawl() { for url := range urlPool { // 并发获取URL }}
func distributeUrls() { // 分批将URL放入channel}

这样通过控制并发数量,重用对象池,使用无锁队列等方法可以优化爬虫程序的并发效率。

 

   

总结

Go 语言并发程序的执行效率对程序性能有重大影响。可从以下几个方面进行优化

  1. 合理利用多核 CPU,并行执行运算密集型任务,设置 GOMAXPROCS;对 IO 密集型任务,采用异步并发调用提升吞吐量。

     

  2. 控制 Goroutine 数量,避免过多线程导致调度器过载;同时也要避免使用过少线程而不能充分利用 CPU。

     

  3. 优化内存占用,重用对象减少 GC 开销;适当降低 Goroutine 栈空间也有助于支持更多 Goroutine。

     

  4. 选择正确的并发安全数据结构,如无锁数据结构、原子操作等可以提升并发效率。

     

  5. 使用调试和性能分析工具定位效率薄弱点;比如 CPU 分析、阻塞分析等。

     

  6. 充分考虑并发模型的优化,比如管道化消息传递;以及设计异步并发流程等。

     

  7. 针对不同场景设计最优的并发模式,避免共享内存同步等开销。

     

  8. 合理设置并发数,最大程度利用硬件资源而不导致过度调度。

     

  9. 采用最优的数据结构,利用无锁并发安全实现。

熟练运用这些优化技巧,可让 Go语言并发程序的效率和性能达到最佳。

 

Go语言并发31 Go语言并发 · 目录 上一篇Go 如何解决 并发中的竞争状态下一篇Go实现并发与并行,这才是正确打开方式! 阅读 1094 Go先锋 ​ 喜欢此内容的人还喜欢   没想到,Go语言垃圾回收是这样工作的!     Go先锋 不看的原因   如何高效地使用goroutine     Go先锋 不看的原因   Golang 内存分配机制详解     路多辛 不看的原因   写留言              

人划线

 

标签:wg,trace,tool,并发,func,go,Go,优化
From: https://www.cnblogs.com/cheyunhua/p/17979835

相关文章

  • k8s_client-go 构建客户端的几种方式
    kubernetesclient-go构建客户端的几种方式packagecallk8simport( "context" "log" metav1"k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/t......
  • WPF 使用CommunityToolkit.Mvvm实现Binding示例
    WPF在国内的发展一言难尽。属于那种死不死,活不活的状态。现在应用最多的场景就是上位机了。最近研究了一下WPF中重要的特性之一Binding。如果你没有学会它,基本WPF就没有学明白。研究Binding的时候,我也用了MVVM特性,这也是WPF必学的科目之一。我原来用的是MVVMLight。可是后来......
  • wpf 使用CommunityToolKit.Mvvm实现绑定验证
    接上一个文章,我们在上一个文章中使用CommunityToolKit.Mvvm写了绑定。我们在这篇文章中,写一下绑定验证。绑定验证在WPF系统中也是非常重要的一环。验证可以使得你的系统变得非常健壮。除非你的系统是游戏级别的自娱自乐级别。要么我都建议你加上验证。还是那句老话,写程序一定要......
  • 新品来袭,全国产ARM+FPGA--"RK3568J+Logos-2"工业核心板,让您的硬件设计“更简单”!
    如需选购,请登录创龙科技天猫旗舰店:tronlong.tmall.com!欢迎加入RK3568J技术交流群:567208221欢迎加入Logos-2技术交流群:311416997更多产品详情以及购买咨询可添加如下客服人员微信(即刻添加,马上咨询) 更多RK3568J+Logos-2产品资料可长按二维码识别下载  ......
  • django使用redis集群、连接池、MySQL连接池
    redis的相关设置CACHES={"default":{"BACKEND":"django_redis.cache.RedisCache","LOCATION":["redis://127.0.0.1:6379/1","redis://127.0.0.1:6380/1",#...],"OPTIONS":{"CLIENT_......
  • 从 Solidity 合约生成 Go 代码
    从Solidity合约生成Go代码在Ethereum开发中,我们经常需要在Go程序中与Solidity智能合约进行交互。这时,我们就需要将Solidity合约的ABI(ApplicationBinaryInterface)转换为Go代码。在本文中,我们将介绍如何使用 abigen 工具来完成这个任务。安装必要的工具首......
  • Go语言核心36讲 38 | bytes包与字节串操作(上)
    我相信,经过上一次的学习,你已经对strings.Builder和strings.Reader这两个类型足够熟悉了。我上次还建议你去自行查阅strings代码包中的其他程序实体。如果你认真去看了,那么肯定会对我们今天要讨论的bytes代码包,有种似曾相识的感觉。前导内容:bytes.Buffer基础知识strings包和b......
  • Go语言核心36讲 37 | strings包与字符串操作
    在上一篇文章中,我介绍了Go语言与Unicode编码规范、UTF-8编码格式的渊源及运用。Go语言不但拥有可以独立代表Unicode字符的类型rune,而且还有可以对字符串值进行Unicode字符拆分的for语句。除此之外,标准库中的unicode包及其子包还提供了很多的函数和数据类型,可以帮助我们解析各......
  • Go语言核心36讲 42 | bufio包中的数据类型 (上)
    今天,我们来讲另一个与I/O操作强相关的代码包bufio。bufio是“bufferedI/O”的缩写。顾名思义,这个代码包中的程序实体实现的I/O操作都内置了缓冲区。bufio包中的数据类型主要有:Reader;Scanner;Writer和ReadWriter。与io包中的数据类型类似,这些类型的值也都需要在初始化的时......
  • Go语言核心36讲 41 | io包中的接口和工具 (下)
    上一篇文章中,我主要讲到了io.Reader的扩展接口和实现类型。当然,io代码包中的核心接口不止io.Reader一个。我们基于它引出的一条主线,只是io包类型体系中的一部分。我们很有必要再从另一个角度去探索一下,以求对io包有更加全面的了解。下面的一个问题就与此有关。知识扩展问题:i......