speedup skip-dirs processing by caching

This commit is contained in:
Denis Isaev 2019-09-14 20:15:11 +03:00
parent 4fd88b9b7e
commit fc8d614b2b
No known key found for this signature in database
GPG Key ID: A36A0EC8E27A1A01
2 changed files with 65 additions and 16 deletions

View File

@ -75,7 +75,7 @@ func NewRunner(astCache *astcache.Cache, cfg *config.Config, log logutils.Log, g
skipDirsProcessor, // must be after path prettifier skipDirsProcessor, // must be after path prettifier
processors.NewAutogeneratedExclude(astCache), processors.NewAutogeneratedExclude(astCache),
processors.NewIdentifierMarker(), // must be befor exclude processors.NewIdentifierMarker(), // must be before exclude because users see already marked output and configure excluding by it
processors.NewExclude(excludeTotalPattern), processors.NewExclude(excludeTotalPattern),
processors.NewExcludeRules(excludeRules, lineCache, log.Child("exclude_rules")), processors.NewExcludeRules(excludeRules, lineCache, log.Child("exclude_rules")),
processors.NewNolint(astCache, log.Child("nolint"), dbManager), processors.NewNolint(astCache, log.Child("nolint"), dbManager),
@ -92,6 +92,8 @@ func NewRunner(astCache *astcache.Cache, cfg *config.Config, log logutils.Log, g
}, nil }, nil
} }
func someUnusedFunc() {}
type lintRes struct { type lintRes struct {
linter *linter.Config linter *linter.Config
err error err error
@ -217,6 +219,11 @@ func (r *Runner) runWorkers(ctx context.Context, lintCtx *linter.Context, linter
return lintResultsCh return lintResultsCh
} }
type processorStat struct {
inCount int
outCount int
}
func (r Runner) processLintResults(inCh <-chan lintRes) <-chan lintRes { func (r Runner) processLintResults(inCh <-chan lintRes) <-chan lintRes {
outCh := make(chan lintRes, 64) outCh := make(chan lintRes, 64)
@ -224,6 +231,7 @@ func (r Runner) processLintResults(inCh <-chan lintRes) <-chan lintRes {
sw := timeutils.NewStopwatch("processing", r.Log) sw := timeutils.NewStopwatch("processing", r.Log)
var issuesBefore, issuesAfter int var issuesBefore, issuesAfter int
statPerProcessor := map[string]processorStat{}
defer close(outCh) defer close(outCh)
for res := range inCh { for res := range inCh {
@ -234,7 +242,7 @@ func (r Runner) processLintResults(inCh <-chan lintRes) <-chan lintRes {
if len(res.issues) != 0 { if len(res.issues) != 0 {
issuesBefore += len(res.issues) issuesBefore += len(res.issues)
res.issues = r.processIssues(res.issues, sw) res.issues = r.processIssues(res.issues, sw, statPerProcessor)
issuesAfter += len(res.issues) issuesAfter += len(res.issues)
outCh <- res outCh <- res
} }
@ -252,12 +260,23 @@ func (r Runner) processLintResults(inCh <-chan lintRes) <-chan lintRes {
if issuesBefore != issuesAfter { if issuesBefore != issuesAfter {
r.Log.Infof("Issues before processing: %d, after processing: %d", issuesBefore, issuesAfter) r.Log.Infof("Issues before processing: %d, after processing: %d", issuesBefore, issuesAfter)
} }
r.printPerProcessorStat(statPerProcessor)
sw.PrintStages() sw.PrintStages()
}() }()
return outCh return outCh
} }
func (r Runner) printPerProcessorStat(stat map[string]processorStat) {
parts := make([]string, 0, len(stat))
for name, ps := range stat {
if ps.inCount != 0 {
parts = append(parts, fmt.Sprintf("%s: %d/%d", name, ps.outCount, ps.inCount))
}
}
r.Log.Infof("Processors filtering stat (out/in): %s", strings.Join(parts, ", "))
}
func collectIssues(resCh <-chan lintRes) <-chan result.Issue { func collectIssues(resCh <-chan lintRes) <-chan result.Issue {
retIssues := make(chan result.Issue, 1024) retIssues := make(chan result.Issue, 1024)
go func() { go func() {
@ -294,7 +313,7 @@ func (r Runner) Run(ctx context.Context, linters []*linter.Config, lintCtx *lint
return collectIssues(processedLintResultsCh) return collectIssues(processedLintResultsCh)
} }
func (r *Runner) processIssues(issues []result.Issue, sw *timeutils.Stopwatch) []result.Issue { func (r *Runner) processIssues(issues []result.Issue, sw *timeutils.Stopwatch, statPerProcessor map[string]processorStat) []result.Issue {
for _, p := range r.Processors { for _, p := range r.Processors {
var newIssues []result.Issue var newIssues []result.Issue
var err error var err error
@ -306,6 +325,10 @@ func (r *Runner) processIssues(issues []result.Issue, sw *timeutils.Stopwatch) [
if err != nil { if err != nil {
r.Log.Warnf("Can't process result by %s processor: %s", p.Name(), err) r.Log.Warnf("Can't process result by %s processor: %s", p.Name(), err)
} else { } else {
stat := statPerProcessor[p.Name()]
stat.inCount += len(issues)
stat.outCount += len(newIssues)
statPerProcessor[p.Name()] = stat
issues = newIssues issues = newIssues
} }

View File

@ -11,11 +11,17 @@ import (
"github.com/golangci/golangci-lint/pkg/result" "github.com/golangci/golangci-lint/pkg/result"
) )
type skipStat struct {
pattern string
count int
}
type SkipDirs struct { type SkipDirs struct {
patterns []*regexp.Regexp patterns []*regexp.Regexp
log logutils.Log log logutils.Log
skippedDirs map[string]string // dir to the last regexp mapping skippedDirs map[string]*skipStat
absArgsDirs []string absArgsDirs []string
skippedDirsCache map[string]bool
} }
var _ Processor = SkipFiles{} var _ Processor = SkipFiles{}
@ -50,14 +56,15 @@ 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]*skipStat{},
absArgsDirs: absArgsDirs, absArgsDirs: absArgsDirs,
skippedDirsCache: map[string]bool{},
}, nil }, nil
} }
func (p SkipDirs) Name() string { func (p *SkipDirs) Name() string {
return "skip_dirs" return "skip_dirs"
} }
@ -78,12 +85,26 @@ func (p *SkipDirs) shouldPassIssue(i *result.Issue) bool {
} }
issueRelDir := filepath.Dir(i.FilePath()) issueRelDir := filepath.Dir(i.FilePath())
if toPass, ok := p.skippedDirsCache[issueRelDir]; ok {
if !toPass {
p.skippedDirs[issueRelDir].count++
}
return toPass
}
issueAbsDir, err := filepath.Abs(issueRelDir) issueAbsDir, err := filepath.Abs(issueRelDir)
if err != nil { if err != nil {
p.log.Warnf("Can't abs-ify path %q: %s", issueRelDir, err) p.log.Warnf("Can't abs-ify path %q: %s", issueRelDir, err)
return true return true
} }
toPass := p.shouldPassIssueDirs(issueRelDir, issueAbsDir)
p.skippedDirsCache[issueRelDir] = toPass
return toPass
}
func (p *SkipDirs) shouldPassIssueDirs(issueRelDir, issueAbsDir string) bool {
for _, absArgDir := range p.absArgsDirs { for _, absArgDir := range p.absArgsDirs {
if absArgDir == issueAbsDir { if absArgDir == issueAbsDir {
// we must not skip issues if they are from explicitly set dirs // we must not skip issues if they are from explicitly set dirs
@ -101,7 +122,12 @@ 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[issueRelDir] = ps if p.skippedDirs[issueRelDir] == nil {
p.skippedDirs[issueRelDir] = &skipStat{
pattern: ps,
}
}
p.skippedDirs[issueRelDir].count++
return false return false
} }
} }
@ -109,8 +135,8 @@ func (p *SkipDirs) shouldPassIssue(i *result.Issue) bool {
return true return true
} }
func (p SkipDirs) Finish() { func (p *SkipDirs) Finish() {
for dir, pattern := range p.skippedDirs { for dir, stat := range p.skippedDirs {
p.log.Infof("Skipped dir %s by pattern %s", dir, pattern) p.log.Infof("Skipped %d issues from dir %s by pattern %s", stat.count, dir, stat.pattern)
} }
} }