From 0b16070ae6599da0e01304cb141cdfaffd781abd Mon Sep 17 00:00:00 2001 From: Giedrius Jonikas Date: Wed, 30 Oct 2024 20:12:58 +0000 Subject: [PATCH] Buffer 'docker stats' text to avoid terminal flickering This change reduces the flickering of the terminal when running `docker stats` by buffering the formatted stats text and printing it in one write. Should also consume less CPU as we now only have to issue a single syscall to write the stats text to the terminal. Signed-off-by: Giedrius Jonikas --- cli/command/container/stats.go | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/cli/command/container/stats.go b/cli/command/container/stats.go index dd5e35d5fae8..1ca14ad4c787 100644 --- a/cli/command/container/stats.go +++ b/cli/command/container/stats.go @@ -1,6 +1,7 @@ package container import ( + "bytes" "context" "fmt" "io" @@ -264,31 +265,40 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions) // so we unlikely hit this code in practice. daemonOSType = dockerCLI.ServerInfo().OSType } + + // Buffer to store formatted stats text. + // Once formatted, it will be printed in one write to avoid screen flickering. + var statsTextBuffer bytes.Buffer + statsCtx := formatter.Context{ - Output: dockerCLI.Out(), + Output: &statsTextBuffer, Format: NewStatsFormat(format, daemonOSType), } - cleanScreen := func() { - if !options.NoStream { - _, _ = fmt.Fprint(dockerCLI.Out(), "\033[2J") - _, _ = fmt.Fprint(dockerCLI.Out(), "\033[H") - } - } var err error ticker := time.NewTicker(500 * time.Millisecond) defer ticker.Stop() for range ticker.C { - cleanScreen() var ccStats []StatsEntry cStats.mu.RLock() for _, c := range cStats.cs { ccStats = append(ccStats, c.GetStatistics()) } cStats.mu.RUnlock() + + if !options.NoStream { + // Start by clearing the screen and moving the cursor to the top-left + _, _ = fmt.Fprint(&statsTextBuffer, "\033[2J\033[H") + } + if err = statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil { break } + + _, _ = fmt.Fprint(dockerCLI.Out(), statsTextBuffer.String()) + + statsTextBuffer.Reset() + if len(cStats.cs) == 0 && !showAll { break }