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

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


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语言并发 · 目录
上一篇Go 如何解决 并发中的竞争状态下一篇Go实现并发与并行,这才是正确打开方式!
阅读 1094
 
 
 
 
 
 
 
 
posted @   技术颜良  阅读(141)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
历史上的今天:
2021-01-22 kube-proxy ipvs模式详解
点击右上角即可分享
微信分享提示