mirror of https://github.com/golang/go.git
internal/lsp/cache: cache isActiveLocked calculation across snapshots
This speeds up workspace initialization time in DegradeClosed memory mode from 3m to 1m by avoiding unnecessary recomputation of results. Change-Id: Ie5df82952d50ab42125defd148136329f0d50a48 Reviewed-on: https://go-review.googlesource.com/c/tools/+/413658 Reviewed-by: Alan Donovan <adonovan@google.com> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
afa4a9562f
commit
d69bac6d88
|
|
@ -224,7 +224,7 @@ func (s *snapshot) workspaceParseMode(id PackageID) source.ParseMode {
|
|||
if s.view.Options().MemoryMode == source.ModeNormal {
|
||||
return source.ParseFull
|
||||
}
|
||||
if s.isActiveLocked(id, nil) {
|
||||
if s.isActiveLocked(id) {
|
||||
return source.ParseFull
|
||||
}
|
||||
return source.ParseExported
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ func (s *snapshot) load(ctx context.Context, allowNetwork bool, scopes ...interf
|
|||
// invalidate the reverse transitive closure of packages that have changed.
|
||||
invalidatedPackages := s.meta.reverseTransitiveClosure(true, loadedIDs...)
|
||||
s.meta = s.meta.Clone(updates)
|
||||
|
||||
s.resetIsActivePackageLocked()
|
||||
// Invalidate any packages we may have associated with this metadata.
|
||||
//
|
||||
// TODO(rfindley): this should not be necessary, as we should have already
|
||||
|
|
|
|||
|
|
@ -112,6 +112,40 @@ func (m goFilesMap) Delete(key parseKey) {
|
|||
m.impl.Delete(key)
|
||||
}
|
||||
|
||||
type isActivePackageCacheMap struct {
|
||||
impl *persistent.Map
|
||||
}
|
||||
|
||||
func newIsActivePackageCacheMap() isActivePackageCacheMap {
|
||||
return isActivePackageCacheMap{
|
||||
impl: persistent.NewMap(func(a, b interface{}) bool {
|
||||
return a.(PackageID) < b.(PackageID)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func (m isActivePackageCacheMap) Clone() isActivePackageCacheMap {
|
||||
return isActivePackageCacheMap{
|
||||
impl: m.impl.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
func (m isActivePackageCacheMap) Destroy() {
|
||||
m.impl.Destroy()
|
||||
}
|
||||
|
||||
func (m isActivePackageCacheMap) Get(key PackageID) (bool, bool) {
|
||||
value, ok := m.impl.Get(key)
|
||||
if !ok {
|
||||
return false, false
|
||||
}
|
||||
return value.(bool), true
|
||||
}
|
||||
|
||||
func (m isActivePackageCacheMap) Set(key PackageID, value bool) {
|
||||
m.impl.Set(key, value, nil)
|
||||
}
|
||||
|
||||
type parseKeysByURIMap struct {
|
||||
impl *persistent.Map
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,27 +225,28 @@ func (s *Session) createView(ctx context.Context, name string, folder span.URI,
|
|||
},
|
||||
}
|
||||
v.snapshot = &snapshot{
|
||||
id: snapshotID,
|
||||
view: v,
|
||||
backgroundCtx: backgroundCtx,
|
||||
cancel: cancel,
|
||||
initializeOnce: &sync.Once{},
|
||||
generation: s.cache.store.Generation(generationName(v, 0)),
|
||||
packages: newPackagesMap(),
|
||||
meta: &metadataGraph{},
|
||||
files: newFilesMap(),
|
||||
goFiles: newGoFilesMap(),
|
||||
parseKeysByURI: newParseKeysByURIMap(),
|
||||
symbols: make(map[span.URI]*symbolHandle),
|
||||
actions: make(map[actionKey]*actionHandle),
|
||||
workspacePackages: make(map[PackageID]PackagePath),
|
||||
unloadableFiles: make(map[span.URI]struct{}),
|
||||
parseModHandles: make(map[span.URI]*parseModHandle),
|
||||
parseWorkHandles: make(map[span.URI]*parseWorkHandle),
|
||||
modTidyHandles: make(map[span.URI]*modTidyHandle),
|
||||
modWhyHandles: make(map[span.URI]*modWhyHandle),
|
||||
knownSubdirs: newKnownDirsSet(),
|
||||
workspace: workspace,
|
||||
id: snapshotID,
|
||||
view: v,
|
||||
backgroundCtx: backgroundCtx,
|
||||
cancel: cancel,
|
||||
initializeOnce: &sync.Once{},
|
||||
generation: s.cache.store.Generation(generationName(v, 0)),
|
||||
packages: newPackagesMap(),
|
||||
meta: &metadataGraph{},
|
||||
files: newFilesMap(),
|
||||
isActivePackageCache: newIsActivePackageCacheMap(),
|
||||
goFiles: newGoFilesMap(),
|
||||
parseKeysByURI: newParseKeysByURIMap(),
|
||||
symbols: make(map[span.URI]*symbolHandle),
|
||||
actions: make(map[actionKey]*actionHandle),
|
||||
workspacePackages: make(map[PackageID]PackagePath),
|
||||
unloadableFiles: make(map[span.URI]struct{}),
|
||||
parseModHandles: make(map[span.URI]*parseModHandle),
|
||||
parseWorkHandles: make(map[span.URI]*parseWorkHandle),
|
||||
modTidyHandles: make(map[span.URI]*modTidyHandle),
|
||||
modWhyHandles: make(map[span.URI]*modWhyHandle),
|
||||
knownSubdirs: newKnownDirsSet(),
|
||||
workspace: workspace,
|
||||
}
|
||||
|
||||
// Initialize the view without blocking.
|
||||
|
|
|
|||
|
|
@ -91,6 +91,10 @@ type snapshot struct {
|
|||
// It may be invalidated when a file's content changes.
|
||||
packages packagesMap
|
||||
|
||||
// isActivePackageCache maps package ID to the cached value if it is active or not.
|
||||
// It may be invalidated when metadata changes or a new file is opened or closed.
|
||||
isActivePackageCache isActivePackageCacheMap
|
||||
|
||||
// actions maps an actionkey to its actionHandle.
|
||||
actions map[actionKey]*actionHandle
|
||||
|
||||
|
|
@ -144,6 +148,7 @@ type actionKey struct {
|
|||
func (s *snapshot) Destroy(destroyedBy string) {
|
||||
s.generation.Destroy(destroyedBy)
|
||||
s.packages.Destroy()
|
||||
s.isActivePackageCache.Destroy()
|
||||
s.files.Destroy()
|
||||
s.goFiles.Destroy()
|
||||
s.parseKeysByURI.Destroy()
|
||||
|
|
@ -754,24 +759,20 @@ func (s *snapshot) activePackageIDs() (ids []PackageID) {
|
|||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
seen := make(map[PackageID]bool)
|
||||
for id := range s.workspacePackages {
|
||||
if s.isActiveLocked(id, seen) {
|
||||
if s.isActiveLocked(id) {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func (s *snapshot) isActiveLocked(id PackageID, seen map[PackageID]bool) (active bool) {
|
||||
if seen == nil {
|
||||
seen = make(map[PackageID]bool)
|
||||
}
|
||||
if seen, ok := seen[id]; ok {
|
||||
func (s *snapshot) isActiveLocked(id PackageID) (active bool) {
|
||||
if seen, ok := s.isActivePackageCache.Get(id); ok {
|
||||
return seen
|
||||
}
|
||||
defer func() {
|
||||
seen[id] = active
|
||||
s.isActivePackageCache.Set(id, active)
|
||||
}()
|
||||
m, ok := s.meta.metadata[id]
|
||||
if !ok {
|
||||
|
|
@ -785,13 +786,18 @@ func (s *snapshot) isActiveLocked(id PackageID, seen map[PackageID]bool) (active
|
|||
// TODO(rfindley): it looks incorrect that we don't also check GoFiles here.
|
||||
// If a CGo file is open, we want to consider the package active.
|
||||
for _, dep := range m.Deps {
|
||||
if s.isActiveLocked(dep, seen) {
|
||||
if s.isActiveLocked(dep) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *snapshot) resetIsActivePackageLocked() {
|
||||
s.isActivePackageCache.Destroy()
|
||||
s.isActivePackageCache = newIsActivePackageCacheMap()
|
||||
}
|
||||
|
||||
const fileExtensions = "go,mod,sum,work"
|
||||
|
||||
func (s *snapshot) fileWatchingGlobPatterns(ctx context.Context) map[string]struct{} {
|
||||
|
|
@ -1287,6 +1293,7 @@ func (s *snapshot) clearShouldLoad(scopes ...interface{}) {
|
|||
}
|
||||
}
|
||||
s.meta = g.Clone(updates)
|
||||
s.resetIsActivePackageLocked()
|
||||
}
|
||||
|
||||
// noValidMetadataForURILocked reports whether there is any valid metadata for
|
||||
|
|
@ -1377,7 +1384,7 @@ func (s *snapshot) openFiles() []source.VersionedFileHandle {
|
|||
|
||||
var open []source.VersionedFileHandle
|
||||
s.files.Range(func(uri span.URI, fh source.VersionedFileHandle) {
|
||||
if s.isOpenLocked(fh.URI()) {
|
||||
if isFileOpen(fh) {
|
||||
open = append(open, fh)
|
||||
}
|
||||
})
|
||||
|
|
@ -1386,6 +1393,10 @@ func (s *snapshot) openFiles() []source.VersionedFileHandle {
|
|||
|
||||
func (s *snapshot) isOpenLocked(uri span.URI) bool {
|
||||
fh, _ := s.files.Get(uri)
|
||||
return isFileOpen(fh)
|
||||
}
|
||||
|
||||
func isFileOpen(fh source.VersionedFileHandle) bool {
|
||||
_, open := fh.(*overlay)
|
||||
return open
|
||||
}
|
||||
|
|
@ -1695,28 +1706,29 @@ func (s *snapshot) clone(ctx, bgCtx context.Context, changes map[span.URI]*fileC
|
|||
newGen := s.view.session.cache.store.Generation(generationName(s.view, s.id+1))
|
||||
bgCtx, cancel := context.WithCancel(bgCtx)
|
||||
result := &snapshot{
|
||||
id: s.id + 1,
|
||||
generation: newGen,
|
||||
view: s.view,
|
||||
backgroundCtx: bgCtx,
|
||||
cancel: cancel,
|
||||
builtin: s.builtin,
|
||||
initializeOnce: s.initializeOnce,
|
||||
initializedErr: s.initializedErr,
|
||||
packages: s.packages.Clone(),
|
||||
actions: make(map[actionKey]*actionHandle, len(s.actions)),
|
||||
files: s.files.Clone(),
|
||||
goFiles: s.goFiles.Clone(),
|
||||
parseKeysByURI: s.parseKeysByURI.Clone(),
|
||||
symbols: make(map[span.URI]*symbolHandle, len(s.symbols)),
|
||||
workspacePackages: make(map[PackageID]PackagePath, len(s.workspacePackages)),
|
||||
unloadableFiles: make(map[span.URI]struct{}, len(s.unloadableFiles)),
|
||||
parseModHandles: make(map[span.URI]*parseModHandle, len(s.parseModHandles)),
|
||||
parseWorkHandles: make(map[span.URI]*parseWorkHandle, len(s.parseWorkHandles)),
|
||||
modTidyHandles: make(map[span.URI]*modTidyHandle, len(s.modTidyHandles)),
|
||||
modWhyHandles: make(map[span.URI]*modWhyHandle, len(s.modWhyHandles)),
|
||||
knownSubdirs: s.knownSubdirs.Clone(),
|
||||
workspace: newWorkspace,
|
||||
id: s.id + 1,
|
||||
generation: newGen,
|
||||
view: s.view,
|
||||
backgroundCtx: bgCtx,
|
||||
cancel: cancel,
|
||||
builtin: s.builtin,
|
||||
initializeOnce: s.initializeOnce,
|
||||
initializedErr: s.initializedErr,
|
||||
packages: s.packages.Clone(),
|
||||
isActivePackageCache: s.isActivePackageCache.Clone(),
|
||||
actions: make(map[actionKey]*actionHandle, len(s.actions)),
|
||||
files: s.files.Clone(),
|
||||
goFiles: s.goFiles.Clone(),
|
||||
parseKeysByURI: s.parseKeysByURI.Clone(),
|
||||
symbols: make(map[span.URI]*symbolHandle, len(s.symbols)),
|
||||
workspacePackages: make(map[PackageID]PackagePath, len(s.workspacePackages)),
|
||||
unloadableFiles: make(map[span.URI]struct{}, len(s.unloadableFiles)),
|
||||
parseModHandles: make(map[span.URI]*parseModHandle, len(s.parseModHandles)),
|
||||
parseWorkHandles: make(map[span.URI]*parseWorkHandle, len(s.parseWorkHandles)),
|
||||
modTidyHandles: make(map[span.URI]*modTidyHandle, len(s.modTidyHandles)),
|
||||
modWhyHandles: make(map[span.URI]*modWhyHandle, len(s.modWhyHandles)),
|
||||
knownSubdirs: s.knownSubdirs.Clone(),
|
||||
workspace: newWorkspace,
|
||||
}
|
||||
|
||||
// Copy all of the FileHandles.
|
||||
|
|
@ -1975,9 +1987,10 @@ func (s *snapshot) clone(ctx, bgCtx context.Context, changes map[span.URI]*fileC
|
|||
result.meta = s.meta
|
||||
}
|
||||
|
||||
// Update workspace packages, if necessary.
|
||||
// Update workspace and active packages, if necessary.
|
||||
if result.meta != s.meta || anyFileOpenedOrClosed {
|
||||
result.workspacePackages = computeWorkspacePackagesLocked(result, result.meta)
|
||||
result.resetIsActivePackageLocked()
|
||||
} else {
|
||||
result.workspacePackages = s.workspacePackages
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue