设为首页 收藏本站
查看: 1608|回复: 0

[经验分享] Golang性能调优入门

[复制链接]

尚未签到

发表于 2018-9-20 07:30:15 | 显示全部楼层 |阅读模式
  如何利用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[0]
  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[94]),
  toS(gcstats.PauseQuantiles[98]),
  toS(gcstats.PauseQuantiles[99]))
  } 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、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-597699-1-1.html 上篇帖子: golang并发 下篇帖子: golang之websocket 源码分析
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表