dev: improve memory tracking
This commit is contained in:
parent
9ce2182d05
commit
396a04630d
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
/*.txt
|
/*.txt
|
||||||
|
/*.pdf
|
||||||
/*.pprof
|
/*.pprof
|
||||||
/dist/
|
/dist/
|
||||||
/.idea/
|
/.idea/
|
||||||
|
16
README.md
16
README.md
@ -341,6 +341,22 @@ On average golangci-lint consumes 26% less memory.
|
|||||||
Golangci-lint directly calls linters (no forking) and reuses 80% of work by parsing program only once.
|
Golangci-lint directly calls linters (no forking) and reuses 80% of work by parsing program only once.
|
||||||
Read [this section](#internals) for details.
|
Read [this section](#internals) for details.
|
||||||
|
|
||||||
|
### Memory Usage of Golangci-lint
|
||||||
|
|
||||||
|
A trade-off between memory usage and execution time can be controlled by [`GOCC`](https://golang.org/pkg/runtime/#hdr-Environment_Variables) environment variable.
|
||||||
|
Less `GOGC` values trigger garbage collection more frequently and golangci-lint consumes less memory and more CPU. Below is the trade-off table for running on this repo:
|
||||||
|
|
||||||
|
|`GOGC`|Peak Memory, GB|Executon Time, s|
|
||||||
|
|------|---------------|----------------|
|
||||||
|
|`5` |1.1 |60 |
|
||||||
|
|`10` |1.1 |34 |
|
||||||
|
|`20` |1.3 |25 |
|
||||||
|
|`30` |1.6 |20.2 |
|
||||||
|
|`50` |2.0 |17.1 |
|
||||||
|
|`80` |2.2 |14.1 |
|
||||||
|
|`100` (default)|2.2 |13.8 |
|
||||||
|
|`off` |3.2 |9.3 |
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
1. Work sharing
|
1. Work sharing
|
||||||
|
@ -310,6 +310,22 @@ On average golangci-lint consumes 26% less memory.
|
|||||||
Golangci-lint directly calls linters (no forking) and reuses 80% of work by parsing program only once.
|
Golangci-lint directly calls linters (no forking) and reuses 80% of work by parsing program only once.
|
||||||
Read [this section](#internals) for details.
|
Read [this section](#internals) for details.
|
||||||
|
|
||||||
|
### Memory Usage of Golangci-lint
|
||||||
|
|
||||||
|
A trade-off between memory usage and execution time can be controlled by [`GOCC`](https://golang.org/pkg/runtime/#hdr-Environment_Variables) environment variable.
|
||||||
|
Less `GOGC` values trigger garbage collection more frequently and golangci-lint consumes less memory and more CPU. Below is the trade-off table for running on this repo:
|
||||||
|
|
||||||
|
|`GOGC`|Peak Memory, GB|Executon Time, s|
|
||||||
|
|------|---------------|----------------|
|
||||||
|
|`5` |1.1 |60 |
|
||||||
|
|`10` |1.1 |34 |
|
||||||
|
|`20` |1.3 |25 |
|
||||||
|
|`30` |1.6 |20.2 |
|
||||||
|
|`50` |2.0 |17.1 |
|
||||||
|
|`80` |2.2 |14.1 |
|
||||||
|
|`100` (default)|2.2 |13.8 |
|
||||||
|
|`off` |3.2 |9.3 |
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
1. Work sharing
|
1. Work sharing
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
@ -30,6 +31,12 @@ func (e *Executor) persistentPreRun(_ *cobra.Command, _ []string) {
|
|||||||
e.log.Fatalf("Can't start CPU profiling: %s", err)
|
e.log.Fatalf("Can't start CPU profiling: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.cfg.Run.MemProfilePath != "" {
|
||||||
|
if rate := os.Getenv("GL_MEMPROFILE_RATE"); rate != "" {
|
||||||
|
runtime.MemProfileRate, _ = strconv.Atoi(rate)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) {
|
func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) {
|
||||||
@ -41,15 +48,45 @@ func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
e.log.Fatalf("Can't create file %s: %s", e.cfg.Run.MemProfilePath, err)
|
e.log.Fatalf("Can't create file %s: %s", e.cfg.Run.MemProfilePath, err)
|
||||||
}
|
}
|
||||||
runtime.GC() // get up-to-date statistics
|
|
||||||
|
var ms runtime.MemStats
|
||||||
|
runtime.ReadMemStats(&ms)
|
||||||
|
printMemStats(&ms, e.log)
|
||||||
|
|
||||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||||
e.log.Fatalf("Can't write heap profile: %s", err)
|
e.log.Fatalf("Can't write heap profile: %s", err)
|
||||||
}
|
}
|
||||||
|
f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(e.exitCode)
|
os.Exit(e.exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printMemStats(ms *runtime.MemStats, logger logutils.Log) {
|
||||||
|
logger.Infof("Mem stats: alloc=%s total_alloc=%s sys=%s "+
|
||||||
|
"heap_alloc=%s heap_sys=%s heap_idle=%s heap_released=%s heap_in_use=%s "+
|
||||||
|
"stack_in_use=%s stack_sys=%s "+
|
||||||
|
"mspan_sys=%s mcache_sys=%s buck_hash_sys=%s gc_sys=%s other_sys=%s "+
|
||||||
|
"mallocs_n=%d frees_n=%d heap_objects_n=%d gc_cpu_fraction=%.2f",
|
||||||
|
formatMemory(ms.Alloc), formatMemory(ms.TotalAlloc), formatMemory(ms.Sys),
|
||||||
|
formatMemory(ms.HeapAlloc), formatMemory(ms.HeapSys),
|
||||||
|
formatMemory(ms.HeapIdle), formatMemory(ms.HeapReleased), formatMemory(ms.HeapInuse),
|
||||||
|
formatMemory(ms.StackInuse), formatMemory(ms.StackSys),
|
||||||
|
formatMemory(ms.MSpanSys), formatMemory(ms.MCacheSys), formatMemory(ms.BuckHashSys),
|
||||||
|
formatMemory(ms.GCSys), formatMemory(ms.OtherSys),
|
||||||
|
ms.Mallocs, ms.Frees, ms.HeapObjects, ms.GCCPUFraction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatMemory(memBytes uint64) string {
|
||||||
|
if memBytes < 1024 {
|
||||||
|
return fmt.Sprintf("%db", memBytes)
|
||||||
|
}
|
||||||
|
if memBytes < 1024*1024 {
|
||||||
|
return fmt.Sprintf("%dkb", memBytes/1024)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%dmb", memBytes/1024/1024)
|
||||||
|
}
|
||||||
|
|
||||||
func getDefaultConcurrency() int {
|
func getDefaultConcurrency() int {
|
||||||
if os.Getenv("HELP_RUN") == "1" {
|
if os.Getenv("HELP_RUN") == "1" {
|
||||||
return 8 // to make stable concurrency for README help generating builds
|
return 8 // to make stable concurrency for README help generating builds
|
||||||
|
@ -430,11 +430,21 @@ func watchResources(ctx context.Context, done chan struct{}, logger logutils.Log
|
|||||||
ticker := time.NewTicker(100 * time.Millisecond)
|
ticker := time.NewTicker(100 * time.Millisecond)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
logEveryRecord := os.Getenv("GL_MEM_LOG_EVERY") == "1"
|
||||||
|
|
||||||
|
track := func() {
|
||||||
var m runtime.MemStats
|
var m runtime.MemStats
|
||||||
runtime.ReadMemStats(&m)
|
runtime.ReadMemStats(&m)
|
||||||
|
|
||||||
|
if logEveryRecord {
|
||||||
|
printMemStats(&m, logger)
|
||||||
|
}
|
||||||
|
|
||||||
rssValues = append(rssValues, m.Sys)
|
rssValues = append(rssValues, m.Sys)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
track()
|
||||||
|
|
||||||
stop := false
|
stop := false
|
||||||
select {
|
select {
|
||||||
@ -447,6 +457,7 @@ func watchResources(ctx context.Context, done chan struct{}, logger logutils.Log
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
track()
|
||||||
|
|
||||||
var avg, max uint64
|
var avg, max uint64
|
||||||
for _, v := range rssValues {
|
for _, v := range rssValues {
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
type SkipDirs struct {
|
type SkipDirs struct {
|
||||||
patterns []*regexp.Regexp
|
patterns []*regexp.Regexp
|
||||||
log logutils.Log
|
log logutils.Log
|
||||||
skippedDirs map[string][]string // regexp to dir mapping
|
skippedDirs map[string]string // dir to the last regexp mapping
|
||||||
absArgsDirs []string
|
absArgsDirs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ func NewSkipDirs(patterns []string, log logutils.Log, runArgs []string) (*SkipDi
|
|||||||
return &SkipDirs{
|
return &SkipDirs{
|
||||||
patterns: patternsRe,
|
patterns: patternsRe,
|
||||||
log: log,
|
log: log,
|
||||||
skippedDirs: map[string][]string{},
|
skippedDirs: map[string]string{},
|
||||||
absArgsDirs: absArgsDirs,
|
absArgsDirs: absArgsDirs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ func (p *SkipDirs) shouldPassIssue(i *result.Issue) bool {
|
|||||||
for _, pattern := range p.patterns {
|
for _, pattern := range p.patterns {
|
||||||
if pattern.MatchString(issueRelDir) {
|
if pattern.MatchString(issueRelDir) {
|
||||||
ps := pattern.String()
|
ps := pattern.String()
|
||||||
p.skippedDirs[ps] = append(p.skippedDirs[ps], issueRelDir)
|
p.skippedDirs[issueRelDir] = ps
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ func (p *SkipDirs) shouldPassIssue(i *result.Issue) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p SkipDirs) Finish() {
|
func (p SkipDirs) Finish() {
|
||||||
for pattern, dirs := range p.skippedDirs {
|
for dir, pattern := range p.skippedDirs {
|
||||||
p.log.Infof("Skipped by pattern %s dirs: %s", pattern, dirs)
|
p.log.Infof("Skipped dir %s by pattern %s", dir, pattern)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user