Skip to content

Commit

Permalink
gopls/internal/cache: memoize dependent hash on analysisNode
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
findleyr committed Oct 3, 2024
1 parent a19eef6 commit d0d0d9e
Showing 1 changed file with 39 additions and 21 deletions.
60 changes: 39 additions & 21 deletions gopls/internal/cache/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,9 @@ type analysisNode struct {
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) }
Expand Down Expand Up @@ -597,6 +600,40 @@ func (an *analysisNode) _import() (*types.Package, error) {
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 {
Expand Down Expand Up @@ -770,27 +807,8 @@ func (an *analysisNode) cacheKey() [sha256.Size]byte {

// 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
Expand Down

0 comments on commit d0d0d9e

Please sign in to comment.