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
	 Denis Isaev
						Denis Isaev