• 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 ", 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 ",

    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 ",

    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)

    }

    }

     

    }

  • 相关阅读:
    TZOJ 1214: 数据结构练习题――线性表操作
    团体程序设计天梯赛 到底是不是太胖了
    TZOJ 数据结构实验:单链表元素插入
    Codeforces Round #504 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final)
    Codeforces Round #511 (Div. 2)
    模板
    模板
    模板
    Codeforces Round #603 (Div. 2)
    模板
  • 原文地址:https://www.cnblogs.com/ghost240/p/3740950.html
Copyright © 2020-2023  润新知