Track memory usage (#379)

This commit is contained in:
Neil O'Toole 2024-01-29 08:16:21 -07:00 committed by GitHub
parent 35f95dd915
commit 90219c9386
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 64 additions and 1 deletions

View File

@ -32,6 +32,7 @@ import (
"github.com/neilotoole/sq/cli/buildinfo" "github.com/neilotoole/sq/cli/buildinfo"
"github.com/neilotoole/sq/cli/flag" "github.com/neilotoole/sq/cli/flag"
"github.com/neilotoole/sq/cli/run" "github.com/neilotoole/sq/cli/run"
"github.com/neilotoole/sq/libsq/core/ioz"
"github.com/neilotoole/sq/libsq/core/lg" "github.com/neilotoole/sq/libsq/core/lg"
"github.com/neilotoole/sq/libsq/core/lg/lga" "github.com/neilotoole/sq/libsq/core/lg/lga"
"github.com/neilotoole/sq/libsq/core/options" "github.com/neilotoole/sq/libsq/core/options"
@ -85,6 +86,7 @@ func ExecuteWith(ctx context.Context, ru *run.Run, args []string) error {
_ = ru.LogCloser() _ = ru.LogCloser()
} }
}() }()
ctx = options.NewContext(ctx, options.Merge(options.FromContext(ctx), ru.Config.Options)) ctx = options.NewContext(ctx, options.Merge(options.FromContext(ctx), ru.Config.Options))
log := lg.FromContext(ctx) log := lg.FromContext(ctx)
log.Info("EXECUTE", "args", strings.Join(args, " ")) log.Info("EXECUTE", "args", strings.Join(args, " "))
@ -95,6 +97,14 @@ func ExecuteWith(ctx context.Context, ru *run.Run, args []string) error {
ctx = run.NewContext(ctx, ru) ctx = run.NewContext(ctx, ru)
if freq := OptDebugTrackMemory.Get(options.FromContext(ctx)); freq > 0 {
// Debug setting to log peak memory usage on exit.
memTracker := ioz.StartPeakMemoryTracker(ctx, freq)
defer func() {
log.Info("Peak memory usage", "mem", memTracker.String(), "bytes", memTracker.Load())
}()
}
rootCmd := newCommandTree(ru) rootCmd := newCommandTree(ru)
var err error var err error

View File

@ -201,6 +201,7 @@ func RegisterDefaultOpts(reg *options.Registry) {
driver.OptIngestSampleSize, driver.OptIngestSampleSize,
csv.OptDelim, csv.OptDelim,
csv.OptEmptyAsNull, csv.OptEmptyAsNull,
OptDebugTrackMemory,
progress.OptDebugSleep, progress.OptDebugSleep,
) )
} }

View File

@ -19,7 +19,7 @@ func TestRegisterDefaultOpts(t *testing.T) {
log.Debug("options.Registry (after)", "reg", reg) log.Debug("options.Registry (after)", "reg", reg)
keys := reg.Keys() keys := reg.Keys()
require.Len(t, keys, 50) require.Len(t, keys, 51)
for _, opt := range reg.Opts() { for _, opt := range reg.Opts() {
opt := opt opt := opt

View File

@ -120,6 +120,16 @@ command, sq falls back to "text". Available formats:
`Delay before showing a progress bar.`, `Delay before showing a progress bar.`,
) )
OptDebugTrackMemory = options.NewDuration(
"debug.stats.frequency",
"",
0,
0,
"Memory usage sampling interval.",
`Memory usage sampling interval. If non-zero, peak memory usage is periodically
sampled, and reported on exit. If zero, memory usage sampling is disabled.`,
)
OptCompact = options.NewBool( OptCompact = options.NewBool(
"compact", "compact",
"", "",

View File

@ -11,12 +11,15 @@ import (
mrand "math/rand" mrand "math/rand"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/a8m/tree" "github.com/a8m/tree"
"github.com/a8m/tree/ostree" "github.com/a8m/tree/ostree"
"github.com/c2h5oh/datasize"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/neilotoole/sq/libsq/core/errz" "github.com/neilotoole/sq/libsq/core/errz"
@ -792,3 +795,42 @@ func countNonDirs(entries []os.DirEntry) (count int) {
} }
return count return count
} }
// PeakMemory is an [atomic.Uint64] that tracks the peak memory usage.
type PeakMemory struct {
atomic.Uint64
}
// String returns a human-friendly representation.
func (p *PeakMemory) String() string {
v := p.Load()
return datasize.ByteSize(v).HR()
}
// StartPeakMemoryTracker starts a goroutine that tracks the peak memory usage,
// per [runtime.MemStats.Sys] and [runtime.ReadMemStats]. The goroutine sleeps
// for sampleFreq between each sample and exits when ctx is done.
func StartPeakMemoryTracker(ctx context.Context, sampleFreq time.Duration) *PeakMemory {
peakMem := &PeakMemory{}
go func() {
ticker := time.NewTicker(sampleFreq)
defer ticker.Stop()
var peak uint64
stats := &runtime.MemStats{}
for {
runtime.ReadMemStats(stats)
peak = peakMem.Load()
if stats.Sys > peak {
peakMem.Store(stats.Sys)
}
select {
case <-ctx.Done():
return
case <-ticker.C:
}
}
}()
return peakMem
}