得过且过 发表于 2018-9-20 07:30:15

Golang性能调优入门

  如何利用golang自带的profile工具进行应用程序的性能调优,前一段时间我做的日志分析系统在线上遇到了一个问题,就是分任务的系统down机了,日志处理延迟了10几个小时,这个时候任务分发系统重启之后开始分发任务,但是一下子就承受了十几个并发任务,导致内存消耗过快,直接吃掉了16G的内存,这可急坏了我啊。所以赶紧开始做性能优化。
  性能优化我主要从以下几个方面进行了测试和调优:

[*]CPU Profiling
[*]Mem Profiling
[*]GC & HEAP
  我采用了如下的profile工具代码:
  package main
  import (
  "fmt"
  "log"
  "os"
  "runtime"
  "runtime/debug"
  "runtime/pprof"
  "strconv"
  "sync/atomic"
  "syscall"
  "time"
  )
  var heapProfileCounter int32
  var startTime = time.Now()
  var pid int
  func init() {
  pid = os.Getpid()
  }
  func StartCPUProfile() {
  f, err := os.Create("cpu-" + strconv.Itoa(pid) + ".pprof")
  if err != nil {
  log.Fatal(err)
  }
  pprof.StartCPUProfile(f)
  }
  func StopCPUProfile() {
  pprof.StopCPUProfile()
  }
  func StartBlockProfile(rate int) {
  runtime.SetBlockProfileRate(rate)
  }
  func StopBlockProfile() {
  filename := "block-" + strconv.Itoa(pid) + ".pprof"
  f, err := os.Create(filename)
  if err != nil {
  log.Fatal(err)
  }
  if err = pprof.Lookup("block").WriteTo(f, 0); err != nil {
  log.Fatalf(" can't write %s: %s", filename, err)
  }
  f.Close()
  }
  func SetMemProfileRate(rate int) {
  runtime.MemProfileRate = rate
  }
  func GC() {
  runtime.GC()
  }
  func DumpHeap() {
  filename := "heap-" + strconv.Itoa(pid) + "-" + strconv.Itoa(int(atomic.AddInt32(&heapProfileCounter, 1))) + ".pprof"
  f, err := os.Create(filename)
  if err != nil {
  fmt.Fprintf(os.Stderr, "testing: %s", err)
  return
  }
  if err = pprof.WriteHeapProfile(f); err != nil {
  fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", filename, err)
  }
  f.Close()
  }
  func showSystemStat(interval time.Duration, count int) {
  usage1 := &syscall.Rusage{}
  var lastUtime int64
  var lastStime int64
  counter := 0
  for {
  //http://man7.org/linux/man-pages/man3/vtimes.3.html
  syscall.Getrusage(syscall.RUSAGE_SELF, usage1)
  utime := usage1.Utime.Sec*1000000000 + usage1.Utime.Usec
  stime := usage1.Stime.Sec*1000000000 + usage1.Stime.Usec
  userCPUUtil := float64(utime-lastUtime) * 100 / float64(interval)
  sysCPUUtil := float64(stime-lastStime) * 100 / float64(interval)
  memUtil := usage1.Maxrss * 1024
  lastUtime = utime
  lastStime = stime
  if counter > 0 {
  fmt.Printf("cpu: %3.2f%% us%3.2f%% sy, mem:%s \n", userCPUUtil, sysCPUUtil, toH(uint64(memUtil)))
  }
  counter += 1
  if count >= 1 && count < counter {
  return
  }
  time.Sleep(interval)
  }
  }
  func ShowSystemStat(seconds int) {
  go func() {
  interval := time.Duration(seconds) * time.Second
  showSystemStat(interval, 0)
  }()
  }
  func PrintSystemStats() {
  interval := time.Duration(1) * time.Second
  showSystemStat(interval, 1)
  }
  func ShowGCStat() {
  go func() {
  var numGC int64
  interval := time.Duration(100) * time.Millisecond
  gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)}
  memStats := &runtime.MemStats{}
  for {
  debug.ReadGCStats(gcstats)
  if gcstats.NumGC > numGC {
  runtime.ReadMemStats(memStats)
  printGC(memStats, gcstats)
  numGC = gcstats.NumGC
  }
  time.Sleep(interval)
  }
  }()
  }
  func PrintGCSummary() {
  memStats := &runtime.MemStats{}
  runtime.ReadMemStats(memStats)
  gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)}
  debug.ReadGCStats(gcstats)
  printGC(memStats, gcstats)
  }
  func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats) {
  if gcstats.NumGC > 0 {
  lastPause := gcstats.Pause
  elapsed := time.Now().Sub(startTime)
  overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100
  allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
  fmt.Printf("NumGC:%d Pause:%s Pause(Avg):%s Overhead:%3.2f%% Alloc:%s Sys:%s Alloc(Rate):%s/s Histogram:%s %s %s \n",
  gcstats.NumGC,
  toS(lastPause),
  toS(avg(gcstats.Pause)),
  overhead,
  toH(memStats.Alloc),
  toH(memStats.Sys),
  toH(uint64(allocatedRate)),
  toS(gcstats.PauseQuantiles),
  toS(gcstats.PauseQuantiles),
  toS(gcstats.PauseQuantiles))
  } else {
  // while GC has disabled
  elapsed := time.Now().Sub(startTime)
  allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
  fmt.Printf("Alloc:%s Sys:%s Alloc(Rate):%s/s\n",
  toH(memStats.Alloc),
  toH(memStats.Sys),
  toH(uint64(allocatedRate)))
  }
  }
  func avg(items []time.Duration) time.Duration {
  var sum time.Duration
  for _, item := range items {
  sum += item
  }
  return time.Duration(int64(sum) / int64(len(items)))
  }
  // human readable format
  func toH(bytes uint64) string {
  switch {
  case bytes < 1024:
  return fmt.Sprintf("�", bytes)
  case bytes < 1024*1024:
  return fmt.Sprintf("%.2fK", float64(bytes)/1024)
  case bytes < 1024*1024*1024:
  return fmt.Sprintf("%.2fM", float64(bytes)/1024/1024)
  default:
  return fmt.Sprintf("%.2fG", float64(bytes)/1024/1024/1024)
  }
  }
  // short string format
  func toS(d time.Duration) string {
  u := uint64(d)
  if u < uint64(time.Second) {
  switch {
  case u == 0:
  return "0"
  case u < uint64(time.Microsecond):
  return fmt.Sprintf("%.2fns", float64(u))
  case u < uint64(time.Millisecond):
  return fmt.Sprintf("%.2fus", float64(u)/1000)
  default:
  return fmt.Sprintf("%.2fms", float64(u)/1000/1000)
  }
  } else {
  switch {
  case u < uint64(time.Minute):
  return fmt.Sprintf("%.2fs", float64(u)/1000/1000/1000)
  case u < uint64(time.Hour):
  return fmt.Sprintf("%.2fm", float64(u)/1000/1000/1000/60)
  default:
  return fmt.Sprintf("%.2fh", float64(u)/1000/1000/1000/60/60)
  }
  }
  }

页: [1]
查看完整版本: Golang性能调优入门