diff --git a/core/prof/profilecenter.go b/core/prof/profilecenter.go index 8b03fd211..14405ab46 100644 --- a/core/prof/profilecenter.go +++ b/core/prof/profilecenter.go @@ -1,13 +1,12 @@ package prof import ( - "bytes" - "strconv" + "fmt" + "strings" "sync" "sync/atomic" "time" - "github.com/olekukonko/tablewriter" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/threading" ) @@ -28,46 +27,15 @@ type ( const flushInterval = 5 * time.Minute -var ( - pc = &profileCenter{ - slots: make(map[string]*profileSlot), - } - once sync.Once -) - -func report(name string, duration time.Duration) { - updated := func() bool { - pc.lock.RLock() - defer pc.lock.RUnlock() - - slot, ok := pc.slots[name] - if ok { - atomic.AddInt64(&slot.lifecount, 1) - atomic.AddInt64(&slot.lastcount, 1) - atomic.AddInt64(&slot.lifecycle, int64(duration)) - atomic.AddInt64(&slot.lastcycle, int64(duration)) - } - return ok - }() - - if !updated { - func() { - pc.lock.Lock() - defer pc.lock.Unlock() - - pc.slots[name] = &profileSlot{ - lifecount: 1, - lastcount: 1, - lifecycle: int64(duration), - lastcycle: int64(duration), - } - }() - } - - once.Do(flushRepeatly) +var pc = &profileCenter{ + slots: make(map[string]*profileSlot), } -func flushRepeatly() { +func init() { + flushRepeatedly() +} + +func flushRepeatedly() { threading.GoSafe(func() { for { time.Sleep(flushInterval) @@ -76,42 +44,64 @@ func flushRepeatly() { }) } +func report(name string, duration time.Duration) { + slot := loadOrStoreSlot(name, duration) + + atomic.AddInt64(&slot.lifecount, 1) + atomic.AddInt64(&slot.lastcount, 1) + atomic.AddInt64(&slot.lifecycle, int64(duration)) + atomic.AddInt64(&slot.lastcycle, int64(duration)) +} + +func loadOrStoreSlot(name string, duration time.Duration) *profileSlot { + pc.lock.RLock() + slot, ok := pc.slots[name] + pc.lock.RUnlock() + + if ok { + return slot + } + + pc.lock.Lock() + defer pc.lock.Unlock() + + // double-check + if slot, ok = pc.slots[name]; ok { + return slot + } + + slot = &profileSlot{} + pc.slots[name] = slot + return slot +} + func generateReport() string { - var buffer bytes.Buffer - buffer.WriteString("Profiling report\n") - var data [][]string + var builder strings.Builder + builder.WriteString("Profiling report\n") + builder.WriteString("QUEUE,LIFECOUNT,LIFECYCLE,LASTCOUNT,LASTCYCLE\n") + calcFn := func(total, count int64) string { if count == 0 { return "-" } - return (time.Duration(total) / time.Duration(count)).String() } - func() { - pc.lock.Lock() - defer pc.lock.Unlock() + pc.lock.Lock() + for key, slot := range pc.slots { + builder.WriteString(fmt.Sprintf("%s,%d,%s,%d,%s\n", + key, + slot.lifecount, + calcFn(slot.lifecycle, slot.lifecount), + slot.lastcount, + calcFn(slot.lastcycle, slot.lastcount), + )) - for key, slot := range pc.slots { - data = append(data, []string{ - key, - strconv.FormatInt(slot.lifecount, 10), - calcFn(slot.lifecycle, slot.lifecount), - strconv.FormatInt(slot.lastcount, 10), - calcFn(slot.lastcycle, slot.lastcount), - }) + // reset last cycle stats + atomic.StoreInt64(&slot.lastcount, 0) + atomic.StoreInt64(&slot.lastcycle, 0) + } + pc.lock.Unlock() - // reset the data for last cycle - slot.lastcount = 0 - slot.lastcycle = 0 - } - }() - - table := tablewriter.NewWriter(&buffer) - table.SetHeader([]string{"QUEUE", "LIFECOUNT", "LIFECYCLE", "LASTCOUNT", "LASTCYCLE"}) - table.SetBorder(false) - table.AppendBulk(data) - table.Render() - - return buffer.String() + return builder.String() } diff --git a/go.mod b/go.mod index e77cbbdd1..047886f43 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/google/uuid v1.6.0 github.com/jackc/pgx/v5 v5.7.4 github.com/jhump/protoreflect v1.17.0 - github.com/olekukonko/tablewriter v0.0.5 github.com/pelletier/go-toml/v2 v2.2.2 github.com/prometheus/client_golang v1.21.1 github.com/redis/go-redis/v9 v9.8.0 diff --git a/go.sum b/go.sum index e8c3c8183..0bba6113b 100644 --- a/go.sum +++ b/go.sum @@ -121,7 +121,6 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -135,8 +134,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=