зеркало из https://github.com/golang/tools.git
gopls/internal/test/marker: seed the cache before running tests
The marker tests are heavily parallelized, and many import common standary library packages. As a result, depending on concurrency, they perform a LOT of duplicate type checking and analysis. Seeding the cache before running the tests resulted in an ~80% decrease in CPU time on my workstation, from ~250s to ~50s, which is close to the ~40s of CPU time observed on the second invocation, which has a cache seeded by the previous run. I also observed a ~33% decrease in run time. Admittedly my workstation has 48 cores, and so I'd expect less of an improvement on smaller machines. Change-Id: Ied15062aa8d847a887cc8293c37cb3399e7a82b6 Reviewed-on: https://go-review.googlesource.com/c/tools/+/588940 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
Родитель
01018ba9ed
Коммит
8d54ca127f
|
@ -26,6 +26,7 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
|
@ -107,6 +108,10 @@ func Test(t *testing.T) {
|
|||
// Opt: use a shared cache.
|
||||
cache := cache.New(nil)
|
||||
|
||||
// Opt: seed the cache and file cache by type-checking and analyzing common
|
||||
// standard library packages.
|
||||
seedCache(t, cache)
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
@ -264,6 +269,65 @@ func Test(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// seedCache populates the file cache by type checking and analyzing standard
|
||||
// library packages that are reachable from tests.
|
||||
//
|
||||
// Most tests are themselves small codebases, and yet may reference large
|
||||
// amounts of standard library code. Since tests are heavily parallelized, they
|
||||
// naively end up type checking and analyzing many of the same standard library
|
||||
// packages. By seeding the cache, we ensure cache hits for these standard
|
||||
// library packages, significantly reducing the amount of work done by each
|
||||
// test.
|
||||
//
|
||||
// The following command was used to determine the set of packages to import
|
||||
// below:
|
||||
//
|
||||
// rm -rf ~/.cache/gopls && \
|
||||
// go test -count=1 ./internal/test/marker -cpuprofile=prof -v
|
||||
//
|
||||
// Look through the individual test timings to see which tests are slow, then
|
||||
// look through the imports of slow tests to see which standard library
|
||||
// packages are imported. Choose high level packages such as go/types that
|
||||
// import others such as fmt or go/ast. After doing so, re-run the command and
|
||||
// verify that the total samples in the collected profile decreased.
|
||||
func seedCache(t *testing.T, cache *cache.Cache) {
|
||||
start := time.Now()
|
||||
|
||||
// The the doc string for details on how this seed was produced.
|
||||
seed := `package p
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
"go/types"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
_ = http.Serve
|
||||
_ = sort.Slice
|
||||
_ types.Type
|
||||
_ testing.T
|
||||
)
|
||||
`
|
||||
|
||||
// Create a test environment for the seed file.
|
||||
env := newEnv(t, cache, map[string][]byte{"p.go": []byte(seed)}, nil, nil, fake.EditorConfig{})
|
||||
// See other TODO: this cleanup logic is too messy.
|
||||
defer env.Editor.Shutdown(context.Background()) // ignore error
|
||||
defer env.Sandbox.Close() // ignore error
|
||||
env.Awaiter.Await(context.Background(), integration.InitialWorkspaceLoad)
|
||||
|
||||
// Opening the file is necessary to trigger analysis.
|
||||
env.OpenFile("p.go")
|
||||
|
||||
// As a checksum, verify that the file has no errors after analysis.
|
||||
// This isn't strictly necessary, but helps avoid incorrect seeding due to
|
||||
// typos.
|
||||
env.AfterChange(integration.NoDiagnostics())
|
||||
|
||||
t.Logf("warming the cache took %s", time.Since(start))
|
||||
}
|
||||
|
||||
// A marker holds state for the execution of a single @marker
|
||||
// annotation in the source.
|
||||
type marker struct {
|
||||
|
|
Загрузка…
Ссылка в новой задаче