Merge pull request #3608 from thaJeztah/formatstats_optimize

Small performance optimizations for formatting stats
This commit is contained in:
Sebastiaan van Stijn 2022-05-17 10:10:14 +02:00 committed by GitHub
commit 53f8ed4bec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 17 deletions

View File

@ -1,7 +1,7 @@
package container
import (
"fmt"
"strconv"
"sync"
"github.com/docker/cli/cli/command/formatter"
@ -43,7 +43,7 @@ type StatsEntry struct {
// Stats represents an entity to store containers statistics synchronously
type Stats struct {
mutex sync.Mutex
mutex sync.RWMutex
StatsEntry
err error
}
@ -51,8 +51,8 @@ type Stats struct {
// GetError returns the container statistics error.
// This is used to determine whether the statistics are valid or not
func (cs *Stats) GetError() error {
cs.mutex.Lock()
defer cs.mutex.Unlock()
cs.mutex.RLock()
defer cs.mutex.RUnlock()
return cs.err
}
@ -94,8 +94,8 @@ func (cs *Stats) SetStatistics(s StatsEntry) {
// GetStatistics returns container statistics with other meta data such as the container name
func (cs *Stats) GetStatistics() StatsEntry {
cs.mutex.Lock()
defer cs.mutex.Unlock()
cs.mutex.RLock()
defer cs.mutex.RUnlock()
return cs.StatsEntry
}
@ -183,7 +183,7 @@ func (c *statsContext) CPUPerc() string {
if c.s.IsInvalid {
return "--"
}
return fmt.Sprintf("%.2f%%", c.s.CPUPercentage)
return formatPercentage(c.s.CPUPercentage)
}
func (c *statsContext) MemUsage() string {
@ -193,33 +193,37 @@ func (c *statsContext) MemUsage() string {
if c.os == winOSType {
return units.BytesSize(c.s.Memory)
}
return fmt.Sprintf("%s / %s", units.BytesSize(c.s.Memory), units.BytesSize(c.s.MemoryLimit))
return units.BytesSize(c.s.Memory) + " / " + units.BytesSize(c.s.MemoryLimit)
}
func (c *statsContext) MemPerc() string {
if c.s.IsInvalid || c.os == winOSType {
return "--"
}
return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage)
return formatPercentage(c.s.MemoryPercentage)
}
func (c *statsContext) NetIO() string {
if c.s.IsInvalid {
return "--"
}
return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.NetworkRx, 3), units.HumanSizeWithPrecision(c.s.NetworkTx, 3))
return units.HumanSizeWithPrecision(c.s.NetworkRx, 3) + " / " + units.HumanSizeWithPrecision(c.s.NetworkTx, 3)
}
func (c *statsContext) BlockIO() string {
if c.s.IsInvalid {
return "--"
}
return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.BlockRead, 3), units.HumanSizeWithPrecision(c.s.BlockWrite, 3))
return units.HumanSizeWithPrecision(c.s.BlockRead, 3) + " / " + units.HumanSizeWithPrecision(c.s.BlockWrite, 3)
}
func (c *statsContext) PIDs() string {
if c.s.IsInvalid || c.os == winOSType {
return "--"
}
return fmt.Sprintf("%d", c.s.PidsCurrent)
return strconv.FormatUint(c.s.PidsCurrent, 10)
}
func formatPercentage(val float64) string {
return strconv.FormatFloat(val, 'f', 2, 64) + "%"
}

View File

@ -308,3 +308,38 @@ func TestContainerStatsContextWriteTrunc(t *testing.T) {
out.Reset()
}
}
func BenchmarkStatsFormat(b *testing.B) {
b.ReportAllocs()
stats := genStats()
for i := 0; i < b.N; i++ {
for _, s := range stats {
_ = s.CPUPerc()
_ = s.MemUsage()
_ = s.MemPerc()
_ = s.NetIO()
_ = s.BlockIO()
_ = s.PIDs()
}
}
}
func genStats() []statsContext {
entry := statsContext{s: StatsEntry{
CPUPercentage: 12.3456789,
Memory: 123.456789,
MemoryLimit: 987.654321,
MemoryPercentage: 12.3456789,
BlockRead: 123.456789,
BlockWrite: 987.654321,
NetworkRx: 123.456789,
NetworkTx: 987.654321,
PidsCurrent: 123456789,
}}
stats := make([]statsContext, 100)
for i := 0; i < 100; i++ {
stats = append(stats, entry)
}
return stats
}

View File

@ -184,13 +184,13 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error {
waitFirst.Wait()
var errs []string
cStats.mu.Lock()
cStats.mu.RLock()
for _, c := range cStats.cs {
if err := c.GetError(); err != nil {
errs = append(errs, err.Error())
}
}
cStats.mu.Unlock()
cStats.mu.RUnlock()
if len(errs) > 0 {
return errors.New(strings.Join(errs, "\n"))
}
@ -221,11 +221,11 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error {
for range ticker.C {
cleanScreen()
ccstats := []StatsEntry{}
cStats.mu.Lock()
cStats.mu.RLock()
for _, c := range cStats.cs {
ccstats = append(ccstats, c.GetStatistics())
}
cStats.mu.Unlock()
cStats.mu.RUnlock()
if err = statsFormatWrite(statsCtx, ccstats, daemonOSType, !opts.noTrunc); err != nil {
break
}

View File

@ -14,7 +14,7 @@ import (
)
type stats struct {
mu sync.Mutex
mu sync.RWMutex
cs []*Stats
}