gopls/internal/cache: memoize dependent hash on analysisNode
Benchmarking demonstrated a nontrivial amount of time spent hashing
dependency information in the computation of analysisNode.cacheKey.
Memoize this hash to reduce cost.
Change-Id: Ic123202fbdf00c9de7b3f697c40f593ef798cd42
Reviewed-on: https://go-review.googlesource.com/c/tools/+/617395
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/gopls/internal/cache/analysis.go b/gopls/internal/cache/analysis.go
index 0d7fc46..9debc60 100644
--- a/gopls/internal/cache/analysis.go
+++ b/gopls/internal/cache/analysis.go
@@ -539,6 +539,9 @@
typesOnce sync.Once // guards lazy population of types and typesErr fields
types *types.Package // type information lazily imported from summary
typesErr error // an error producing type information
+
+ depHashOnce sync.Once
+ _depHash file.Hash // memoized hash of data affecting dependents
}
func (an *analysisNode) String() string { return string(an.mp.ID) }
@@ -597,6 +600,40 @@
return an.types, an.typesErr
}
+// depHash computes the hash of node information that may affect other nodes
+// depending on this node: the package path, export hash, and action results.
+//
+// The result is memoized to avoid redundant work when analysing multiple
+// dependents.
+func (an *analysisNode) depHash() file.Hash {
+ an.depHashOnce.Do(func() {
+ hasher := sha256.New()
+ fmt.Fprintf(hasher, "dep: %s\n", an.mp.PkgPath)
+ fmt.Fprintf(hasher, "export: %s\n", an.summary.DeepExportHash)
+
+ // action results: errors and facts
+ actions := an.summary.Actions
+ names := make([]string, 0, len(actions))
+ for name := range actions {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ for _, name := range names {
+ summary := actions[name]
+ fmt.Fprintf(hasher, "action %s\n", name)
+ if summary.Err != "" {
+ fmt.Fprintf(hasher, "error %s\n", summary.Err)
+ } else {
+ fmt.Fprintf(hasher, "facts %s\n", summary.FactsHash)
+ // We can safely omit summary.diagnostics
+ // from the key since they have no downstream effect.
+ }
+ }
+ hasher.Sum(an._depHash[:0])
+ })
+ return an._depHash
+}
+
// analyzeSummary is a gob-serializable summary of successfully
// applying a list of analyzers to a package.
type analyzeSummary struct {
@@ -770,27 +807,8 @@
// vdeps, in PackageID order
for _, vdep := range moremaps.Sorted(an.succs) {
- fmt.Fprintf(hasher, "dep: %s\n", vdep.mp.PkgPath)
- fmt.Fprintf(hasher, "export: %s\n", vdep.summary.DeepExportHash)
-
- // action results: errors and facts
- actions := vdep.summary.Actions
- names := make([]string, 0, len(actions))
- for name := range actions {
- names = append(names, name)
- }
- sort.Strings(names)
- for _, name := range names {
- summary := actions[name]
- fmt.Fprintf(hasher, "action %s\n", name)
- if summary.Err != "" {
- fmt.Fprintf(hasher, "error %s\n", summary.Err)
- } else {
- fmt.Fprintf(hasher, "facts %s\n", summary.FactsHash)
- // We can safely omit summary.diagnostics
- // from the key since they have no downstream effect.
- }
- }
+ hash := vdep.depHash()
+ hasher.Write(hash[:])
}
var hash [sha256.Size]byte