Skip to content

Commit ee41205

Browse files
trying to figure out why occationally dce doesn't seem to be cached correctly
1 parent 087d707 commit ee41205

File tree

3 files changed

+128
-38
lines changed

3 files changed

+128
-38
lines changed

build/build.go

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,38 +1111,11 @@ func (s *Session) LoadPackages(pkg *PackageData) (*sources.Sources, error) {
11111111
FileSet: fileSet,
11121112
JSFiles: append(pkg.JSFiles, overlayJsFiles...),
11131113
}
1114+
s.readDeclCache(pkg)
11141115

11151116
// Add the sources to the session's sources map.
11161117
s.sources[pkg.ImportPath] = srcs
11171118

1118-
// Try to load the cached decls from the build cache.
1119-
if s.buildCache != nil && !pkg.NoCache {
1120-
declCache := &compiler.DeclCache{}
1121-
1122-
// If none of the dependencies were stale, then try to populate the cache.
1123-
// Calculate the most recent mod time of the package's sources
1124-
// and the gopherjs executable.
1125-
// If the cache is older than that or fails to load, then the cache is stale.
1126-
if !pkg.StaleCache {
1127-
var srcModTime time.Time
1128-
if exeModTime := getExeModTime(); exeModTime.After(srcModTime) {
1129-
srcModTime = exeModTime
1130-
}
1131-
if fileModTime := pkg.FileModTime(); fileModTime.After(srcModTime) {
1132-
srcModTime = fileModTime
1133-
}
1134-
1135-
if !s.buildCache.Load(declCache, pkg.ImportPath, srcModTime) {
1136-
pkg.StaleCache = true
1137-
// set a new empty declCache incase the Load left it in a
1138-
// partially filled state.
1139-
declCache = &compiler.DeclCache{}
1140-
}
1141-
}
1142-
1143-
s.cachedDecls[pkg.ImportPath] = declCache
1144-
}
1145-
11461119
// Import dependencies from the augmented files,
11471120
// whilst skipping any that have been already imported.
11481121
for _, importedPkgPath := range srcs.UnresolvedImports(pkg.Imports...) {
@@ -1155,6 +1128,48 @@ func (s *Session) LoadPackages(pkg *PackageData) (*sources.Sources, error) {
11551128
return srcs, nil
11561129
}
11571130

1131+
// readDeclCache tries to load the cached decls from the build cache
1132+
// for the given package.
1133+
func (s *Session) readDeclCache(pkg *PackageData) {
1134+
if s.buildCache == nil {
1135+
return // Caching is disabled for this build
1136+
}
1137+
1138+
if pkg.NoCache {
1139+
log.Infof(`Cache disabled for package %q`, pkg.ImportPath)
1140+
return
1141+
}
1142+
1143+
if pkg.StaleCache {
1144+
s.cachedDecls[pkg.ImportPath] = &compiler.DeclCache{}
1145+
log.Infof(`Cache stale for package %q`, pkg.ImportPath)
1146+
return
1147+
}
1148+
1149+
// Since none of the dependencies were stale, try to populate the cache.
1150+
// Calculate the most recent mod time of the package's sources
1151+
// and the gopherjs executable.
1152+
// If the cache is older than that or fails to load, then the cache is stale.
1153+
var srcModTime time.Time
1154+
if exeModTime := getExeModTime(); exeModTime.After(srcModTime) {
1155+
srcModTime = exeModTime
1156+
}
1157+
if fileModTime := pkg.FileModTime(); fileModTime.After(srcModTime) {
1158+
srcModTime = fileModTime
1159+
}
1160+
1161+
declCache := &compiler.DeclCache{}
1162+
if !s.buildCache.Load(declCache, pkg.ImportPath, srcModTime) {
1163+
// Load failed so this cache is stale and any package depending on this
1164+
// package will also be considered stale.
1165+
pkg.StaleCache = true
1166+
// set a new empty declCache incase the Load left the declCache in a
1167+
// partially filled state.
1168+
declCache = &compiler.DeclCache{}
1169+
}
1170+
s.cachedDecls[pkg.ImportPath] = declCache
1171+
}
1172+
11581173
// PrepareAndCompilePackages prepares and compiles all packages in the session.
11591174
// Thia assumes that all packages including dependencies have already been loaded already.
11601175
// This will return the compiled archive for the rootSrcs package.
@@ -1175,6 +1190,8 @@ func (s *Session) PrepareAndCompilePackages(rootSrcs *sources.Sources) (*compile
11751190
}
11761191
}
11771192

1193+
log.Infof(`DeclCache stats: %s, [global]`, compiler.GlobalDeclCacheStats())
1194+
11781195
rootArchive, ok := s.UpToDateArchives[rootSrcs.ImportPath]
11791196
if !ok {
11801197
// This is confirmation that the root package is in the sources map and got compiled.
@@ -1205,9 +1222,12 @@ func (s *Session) CompilePackage(srcs *sources.Sources, tContext *types.Context)
12051222
// Skip storing cache if the sources haven't changed since read from cache
12061223
// or the cache is disabled for this package (i.e. `dc` is nil).
12071224
if s.buildCache != nil {
1208-
if dc := s.cachedDecls[srcs.ImportPath]; dc != nil && dc.Changed() {
1225+
dc := s.cachedDecls[srcs.ImportPath]
1226+
if dc != nil && dc.Changed() {
12091227
s.buildCache.Store(dc, srcs.ImportPath, time.Now())
12101228
}
1229+
// Uncomment to get statics for individual packages cache usage.
1230+
// log.Infof(`DeclCache stats: %s, %q`, dc.String(), srcs.ImportPath)
12111231
}
12121232

12131233
s.UpToDateArchives[srcs.ImportPath] = archive

compiler/declCache.go

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,26 @@ package compiler
33
import (
44
"fmt"
55
"sort"
6+
"time"
67
)
78

89
type DeclCache struct {
910
decls map[string]*Decl
1011
changed bool
12+
stats declCacheStats
13+
}
14+
15+
var declCacheGlobalStats declCacheStats
16+
17+
type declCacheStats struct {
18+
reads int // number of times Read was called (should be 0 or 1 unless global)
19+
writes int // number of times Write was called (should be 0 or 1 unless global)
20+
loadedCount int // number of declarations loaded from storage
21+
addedCount int // number of declarations added to the cache
22+
skippedCount int // number of declarations not cached (for being generic or non-unique)
23+
usedCount int // number of times a declaration was retrieved from the cache
24+
readDur time.Duration
25+
writeDur time.Duration
1126
}
1227

1328
// Chacnged reports whether the cache has had declarations added to it since
@@ -25,7 +40,12 @@ func (dc *DeclCache) GetDecl(fullname string) *Decl {
2540
if dc == nil {
2641
return nil // cache is disabled
2742
}
28-
return dc.decls[fullname]
43+
if decl, ok := dc.decls[fullname]; ok {
44+
dc.stats.usedCount++
45+
declCacheGlobalStats.usedCount++
46+
return decl
47+
}
48+
return nil
2949
}
3050

3151
func (dc *DeclCache) PutDecl(decl *Decl) {
@@ -44,32 +64,49 @@ func (dc *DeclCache) PutDecl(decl *Decl) {
4464
// Therefore, if the package depending on this one changes a type from
4565
// being blocking to non-blocking or vice versa, the cached declaration
4666
// may be invalid.
67+
//
68+
// TODO(grantnelson-wf): We could improve this by tracking which packages
69+
// the type arguments come from and allow caching if those are all from
70+
// dependencies. Since we don't use cache when a dependency changes,
71+
// (i.e. has stale cache) we can use generics in that case. We just can't
72+
// cache the instantiations that come from packages depending on this one
73+
// since those don't invalidate dependent packages' caches.
74+
dc.stats.skippedCount++
75+
declCacheGlobalStats.skippedCount++
4776
return
4877
}
4978

50-
if isUnqueDeclFullName(decl.FullName) {
79+
if !isUniqueDeclFullName(decl.FullName) {
5180
// Only cache declarations with unique names.
81+
dc.stats.skippedCount++
82+
declCacheGlobalStats.skippedCount++
5283
return
5384
}
5485

55-
if dc.decls == nil {
56-
dc.decls = map[string]*Decl{}
57-
}
5886
if existing, ok := dc.decls[decl.FullName]; ok {
5987
if existing != decl {
6088
panic(fmt.Errorf(`decl cache conflict: different decls with same name: %q`, decl.FullName))
6189
}
6290
return
6391
}
92+
93+
if dc.decls == nil {
94+
dc.decls = map[string]*Decl{}
95+
}
6496
dc.decls[decl.FullName] = decl
6597
dc.changed = true
98+
dc.stats.addedCount++
99+
declCacheGlobalStats.addedCount++
66100
}
67101

68102
func (dc *DeclCache) Read(decode func(any) error) error {
69103
if dc == nil {
70104
return nil // cache is disabled
71105
}
72106

107+
dc.stats.reads++
108+
declCacheGlobalStats.reads++
109+
start := time.Now()
73110
var count int
74111
if err := decode(&count); err != nil {
75112
return err
@@ -83,12 +120,18 @@ func (dc *DeclCache) Read(decode func(any) error) error {
83120
if err := decode(decl); err != nil {
84121
return err
85122
}
86-
if decl.ForGeneric || isUnqueDeclFullName(decl.FullName) {
123+
if decl.ForGeneric || !isUniqueDeclFullName(decl.FullName) {
87124
// These declarations should not have been cached.
88125
continue
89126
}
90127
dc.decls[decl.FullName] = decl
91128
}
129+
130+
dc.stats.loadedCount += len(dc.decls)
131+
declCacheGlobalStats.loadedCount += len(dc.decls)
132+
dur := time.Since(start)
133+
dc.stats.readDur += dur
134+
declCacheGlobalStats.readDur += dur
92135
return nil
93136
}
94137

@@ -97,6 +140,9 @@ func (dc *DeclCache) Write(encode func(any) error) error {
97140
return nil // cache is disabled
98141
}
99142

143+
dc.stats.writes++
144+
declCacheGlobalStats.writes++
145+
start := time.Now()
100146
count := len(dc.decls)
101147
if err := encode(count); err != nil {
102148
return err
@@ -113,5 +159,28 @@ func (dc *DeclCache) Write(encode func(any) error) error {
113159
return err
114160
}
115161
}
162+
dur := time.Since(start)
163+
dc.stats.writeDur += dur
164+
declCacheGlobalStats.writeDur += dur
116165
return nil
117166
}
167+
168+
func (dc *DeclCache) String() string {
169+
if dc == nil {
170+
return `[disabled]`
171+
}
172+
return dc.stats.String()
173+
}
174+
175+
func (s declCacheStats) String() string {
176+
return fmt.Sprintf(`reads=%d, writes=%d, `+
177+
`loaded=%d, added=%d, skipped=%d, used=%d, `+
178+
`readDur=%s, writeDur=%s`,
179+
s.reads, s.writes,
180+
s.loadedCount, s.addedCount, s.skippedCount, s.usedCount,
181+
s.readDur.String(), s.writeDur.String())
182+
}
183+
184+
func GlobalDeclCacheStats() string {
185+
return declCacheGlobalStats.String()
186+
}

compiler/declNames.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,16 @@ func declFullNameDiscriminator(pos token.Pos, fSet *token.FileSet) string {
133133
return fmt.Sprintf("@%s:%d:%d", fileName, p.Line, p.Column)
134134
}
135135

136-
// isUnqueDeclFullName reports whether the given declaration full name is unique.
136+
// isUniqueDeclFullName reports whether the given declaration full name is unique.
137137
// A unique declaration name can be safely cached and reused.
138-
func isUnqueDeclFullName(name string) bool {
138+
func isUniqueDeclFullName(name string) bool {
139139
switch name {
140140
case ``, varDeclNotUniqueFullName, funcVarDeclNotUniqueFullName,
141141
mainFuncDeclNotUniqueFullName, funcDeclNotUniqueFullName:
142142
return false // not unique since it equals one of the known non-unique names
143143
}
144144

145-
// Check if the name is for a nested type declaration without a position discriminator.
145+
// If the name is for a nested type declaration without a position discriminator,
146+
// then it is not unique, otherwise it should be unique.
146147
return !strings.HasPrefix(name, typeDeclNestedPrefix) || strings.Contains(name, "@")
147148
}

0 commit comments

Comments
 (0)