Skip to content

Commit 396a046

Browse files
committed
dev: improve memory tracking
1 parent 9ce2182 commit 396a046

File tree

6 files changed

+88
-7
lines changed

6 files changed

+88
-7
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*.txt
2+
/*.pdf
23
/*.pprof
34
/dist/
45
/.idea/

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,22 @@ On average golangci-lint consumes 26% less memory.
341341
Golangci-lint directly calls linters (no forking) and reuses 80% of work by parsing program only once.
342342
Read [this section](#internals) for details.
343343
344+
### Memory Usage of Golangci-lint
345+
346+
A trade-off between memory usage and execution time can be controlled by [`GOCC`](https://golang.org/pkg/runtime/#hdr-Environment_Variables) environment variable.
347+
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:
348+
349+
|`GOGC`|Peak Memory, GB|Executon Time, s|
350+
|------|---------------|----------------|
351+
|`5` |1.1 |60 |
352+
|`10` |1.1 |34 |
353+
|`20` |1.3 |25 |
354+
|`30` |1.6 |20.2 |
355+
|`50` |2.0 |17.1 |
356+
|`80` |2.2 |14.1 |
357+
|`100` (default)|2.2 |13.8 |
358+
|`off` |3.2 |9.3 |
359+
344360
## Internals
345361
346362
1. Work sharing

README.tmpl.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,22 @@ On average golangci-lint consumes 26% less memory.
310310
Golangci-lint directly calls linters (no forking) and reuses 80% of work by parsing program only once.
311311
Read [this section](#internals) for details.
312312

313+
### Memory Usage of Golangci-lint
314+
315+
A trade-off between memory usage and execution time can be controlled by [`GOCC`](https://golang.org/pkg/runtime/#hdr-Environment_Variables) environment variable.
316+
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:
317+
318+
|`GOGC`|Peak Memory, GB|Executon Time, s|
319+
|------|---------------|----------------|
320+
|`5` |1.1 |60 |
321+
|`10` |1.1 |34 |
322+
|`20` |1.3 |25 |
323+
|`30` |1.6 |20.2 |
324+
|`50` |2.0 |17.1 |
325+
|`80` |2.2 |14.1 |
326+
|`100` (default)|2.2 |13.8 |
327+
|`off` |3.2 |9.3 |
328+
313329
## Internals
314330

315331
1. Work sharing

pkg/commands/root.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66
"runtime"
77
"runtime/pprof"
8+
"strconv"
89

910
"github.com/spf13/cobra"
1011
"github.com/spf13/pflag"
@@ -30,6 +31,12 @@ func (e *Executor) persistentPreRun(_ *cobra.Command, _ []string) {
3031
e.log.Fatalf("Can't start CPU profiling: %s", err)
3132
}
3233
}
34+
35+
if e.cfg.Run.MemProfilePath != "" {
36+
if rate := os.Getenv("GL_MEMPROFILE_RATE"); rate != "" {
37+
runtime.MemProfileRate, _ = strconv.Atoi(rate)
38+
}
39+
}
3340
}
3441

3542
func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) {
@@ -41,15 +48,45 @@ func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) {
4148
if err != nil {
4249
e.log.Fatalf("Can't create file %s: %s", e.cfg.Run.MemProfilePath, err)
4350
}
44-
runtime.GC() // get up-to-date statistics
51+
52+
var ms runtime.MemStats
53+
runtime.ReadMemStats(&ms)
54+
printMemStats(&ms, e.log)
55+
4556
if err := pprof.WriteHeapProfile(f); err != nil {
4657
e.log.Fatalf("Can't write heap profile: %s", err)
4758
}
59+
f.Close()
4860
}
4961

5062
os.Exit(e.exitCode)
5163
}
5264

65+
func printMemStats(ms *runtime.MemStats, logger logutils.Log) {
66+
logger.Infof("Mem stats: alloc=%s total_alloc=%s sys=%s "+
67+
"heap_alloc=%s heap_sys=%s heap_idle=%s heap_released=%s heap_in_use=%s "+
68+
"stack_in_use=%s stack_sys=%s "+
69+
"mspan_sys=%s mcache_sys=%s buck_hash_sys=%s gc_sys=%s other_sys=%s "+
70+
"mallocs_n=%d frees_n=%d heap_objects_n=%d gc_cpu_fraction=%.2f",
71+
formatMemory(ms.Alloc), formatMemory(ms.TotalAlloc), formatMemory(ms.Sys),
72+
formatMemory(ms.HeapAlloc), formatMemory(ms.HeapSys),
73+
formatMemory(ms.HeapIdle), formatMemory(ms.HeapReleased), formatMemory(ms.HeapInuse),
74+
formatMemory(ms.StackInuse), formatMemory(ms.StackSys),
75+
formatMemory(ms.MSpanSys), formatMemory(ms.MCacheSys), formatMemory(ms.BuckHashSys),
76+
formatMemory(ms.GCSys), formatMemory(ms.OtherSys),
77+
ms.Mallocs, ms.Frees, ms.HeapObjects, ms.GCCPUFraction)
78+
}
79+
80+
func formatMemory(memBytes uint64) string {
81+
if memBytes < 1024 {
82+
return fmt.Sprintf("%db", memBytes)
83+
}
84+
if memBytes < 1024*1024 {
85+
return fmt.Sprintf("%dkb", memBytes/1024)
86+
}
87+
return fmt.Sprintf("%dmb", memBytes/1024/1024)
88+
}
89+
5390
func getDefaultConcurrency() int {
5491
if os.Getenv("HELP_RUN") == "1" {
5592
return 8 // to make stable concurrency for README help generating builds

pkg/commands/run.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,11 +430,21 @@ func watchResources(ctx context.Context, done chan struct{}, logger logutils.Log
430430
ticker := time.NewTicker(100 * time.Millisecond)
431431
defer ticker.Stop()
432432

433-
for {
433+
logEveryRecord := os.Getenv("GL_MEM_LOG_EVERY") == "1"
434+
435+
track := func() {
434436
var m runtime.MemStats
435437
runtime.ReadMemStats(&m)
436438

439+
if logEveryRecord {
440+
printMemStats(&m, logger)
441+
}
442+
437443
rssValues = append(rssValues, m.Sys)
444+
}
445+
446+
for {
447+
track()
438448

439449
stop := false
440450
select {
@@ -447,6 +457,7 @@ func watchResources(ctx context.Context, done chan struct{}, logger logutils.Log
447457
break
448458
}
449459
}
460+
track()
450461

451462
var avg, max uint64
452463
for _, v := range rssValues {

pkg/result/processors/skip_dirs.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
type SkipDirs struct {
1515
patterns []*regexp.Regexp
1616
log logutils.Log
17-
skippedDirs map[string][]string // regexp to dir mapping
17+
skippedDirs map[string]string // dir to the last regexp mapping
1818
absArgsDirs []string
1919
}
2020

@@ -52,7 +52,7 @@ func NewSkipDirs(patterns []string, log logutils.Log, runArgs []string) (*SkipDi
5252
return &SkipDirs{
5353
patterns: patternsRe,
5454
log: log,
55-
skippedDirs: map[string][]string{},
55+
skippedDirs: map[string]string{},
5656
absArgsDirs: absArgsDirs,
5757
}, nil
5858
}
@@ -99,7 +99,7 @@ func (p *SkipDirs) shouldPassIssue(i *result.Issue) bool {
9999
for _, pattern := range p.patterns {
100100
if pattern.MatchString(issueRelDir) {
101101
ps := pattern.String()
102-
p.skippedDirs[ps] = append(p.skippedDirs[ps], issueRelDir)
102+
p.skippedDirs[issueRelDir] = ps
103103
return false
104104
}
105105
}
@@ -108,7 +108,7 @@ func (p *SkipDirs) shouldPassIssue(i *result.Issue) bool {
108108
}
109109

110110
func (p SkipDirs) Finish() {
111-
for pattern, dirs := range p.skippedDirs {
112-
p.log.Infof("Skipped by pattern %s dirs: %s", pattern, dirs)
111+
for dir, pattern := range p.skippedDirs {
112+
p.log.Infof("Skipped dir %s by pattern %s", dir, pattern)
113113
}
114114
}

0 commit comments

Comments
 (0)