golangci-lint

Performance

Memory Usage

A trade-off between memory usage and execution time can be controlled by GOGC 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:

GOGCPeak Memory, GBExecution Time, s
51.160
101.134
201.325
301.620.2
502.017.1
802.214.1
100 (default)2.213.8
off3.29.3

Why golangci-lint is so fast

  1. Work sharing

    During operation, golangci-lint shares work between specific linters (like golint, govet, etc.). We don't fork to call a specific linter, but instead use its API. For small and medium projects 50-90% of work between linters can be reused.

    • load []*packages.Package by go/packages once

      We load program (parsing all files and type-checking) only once for all linters. For the most of linters it's the most heavy operation: it takes 5 seconds on 8 kLoC repo and 11 seconds on $GOROOT/src.

    • build ssa.Program once

      Some linters (megacheck, interfacer, unparam) work on SSA representation. Building of this representation takes 1.5 seconds on 8 kLoC repo and 6 seconds on $GOROOT/src.

    • parse source code and build AST once

      Parsing one source file takes 200 us on average. Parsing of all files in $GOROOT/src takes 2 seconds. Currently we parse each file more than once because it's not the bottleneck. But we already save a lot of extra parsing. We're planning to parse each file only once.

    • walk files and directories once

      It takes 300-1000 ms for $GOROOT/src.

  2. Smart linters scheduling

    We schedule linters by a special algorithm which takes estimated execution time into account. It allows to save 10-30% of time when one of heavy linters (megacheck etc) is enabled.

  3. Don't fork to run shell commands

All linters has their version fixed with go modules, they are builtin and you don't need to install them separately.

Edit this page on GitHub