Skip to content

Commit 2ec947a

Browse files
connected up cache 5 trial
1 parent 142181f commit 2ec947a

File tree

6 files changed

+157
-233
lines changed

6 files changed

+157
-233
lines changed

build/build.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,8 +1140,10 @@ func (s *Session) readDeclCache(pkg *PackageData) {
11401140
return
11411141
}
11421142

1143+
declCache := compiler.NewDeclCache(pkg.ImportPath)
1144+
11431145
if pkg.StaleCache {
1144-
s.cachedDecls[pkg.ImportPath] = &compiler.DeclCache{}
1146+
s.cachedDecls[pkg.ImportPath] = declCache
11451147
log.Infof(`Cache stale for package %q`, pkg.ImportPath)
11461148
return
11471149
}
@@ -1158,14 +1160,11 @@ func (s *Session) readDeclCache(pkg *PackageData) {
11581160
srcModTime = fileModTime
11591161
}
11601162

1161-
declCache := &compiler.DeclCache{}
11621163
if !s.buildCache.Load(declCache, pkg.ImportPath, srcModTime) {
11631164
// Load failed so this cache is stale and any package depending on this
1164-
// package will also be considered stale.
1165+
// package will also be considered stale. The declCache will be empty
1166+
// since it doesn't allow partial population.
11651167
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{}
11691168
}
11701169
s.cachedDecls[pkg.ImportPath] = declCache
11711170
}

compiler/declCache.go

Lines changed: 110 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,143 +1,134 @@
11
package compiler
22

33
import (
4+
"errors"
45
"fmt"
5-
"sort"
66
"time"
77
)
88

99
type DeclCache struct {
10-
decls map[string]*Decl
10+
importPath string
11+
importedPaths []string
12+
importDecls []*Decl
13+
typeDecls []*Decl
14+
varDecls []*Decl
15+
funcDecls []*Decl
16+
1117
changed bool
1218
stats declCacheStats
1319
}
1420

21+
// NewDeclCache creates a new empty enabled decl cache for the package with
22+
// the given import path.
23+
//
24+
// The given import path is used as a secondary check to ensure we do not get
25+
// hashing collisions when reading a cache from storage.
26+
func NewDeclCache(importPath string) *DeclCache {
27+
return &DeclCache{importPath: importPath}
28+
}
29+
1530
var declCacheGlobalStats declCacheStats
1631

1732
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
33+
reads int // number of times Read was called (should be 0 or 1 unless global)
34+
writes int // number of times Write was called (should be 0 or 1 unless global)
35+
readCount int // number of declarations loaded from storage
36+
writeCount int // number of declarations written to storage
37+
readDur time.Duration
38+
writeDur time.Duration
2639
}
2740

28-
// Chacnged reports whether the cache has had declarations added to it since
29-
// it was created or last read from storage or created empty.
30-
//
31-
// If there have been no changes, the cache does not need to be written back
32-
// to storage. Typically declarations will only be added when the cache is
33-
// empty and not loaded from storage since otherwise all the needed declarations
34-
// (other than those not being cached) would already be present.
35-
func (dc *DeclCache) Changed() bool {
36-
return dc != nil && dc.changed
37-
}
41+
var (
42+
errReadDisabled = errors.New(`attempted to read a DeclCache that is disabled (nil)`)
43+
errWriteDisabled = errors.New(`attempted to write a DeclCache that is disabled (nil)`)
44+
)
3845

39-
func (dc *DeclCache) GetDecl(fullname string) *Decl {
46+
// ImportPath is the import path for the package this cache is for.
47+
func (dc *DeclCache) ImportPath() string {
4048
if dc == nil {
41-
return nil // cache is disabled
49+
return `[disabled]`
4250
}
43-
if decl, ok := dc.decls[fullname]; ok {
44-
dc.stats.usedCount++
45-
declCacheGlobalStats.usedCount++
51+
return dc.importPath
52+
}
4653

47-
fmt.Printf(">>> GetDecl(%q): Found\n", fullname) // TODO(grantnelson-wf): REMOVE
48-
return decl
49-
}
54+
// Changed reports whether the cache has had declarations set to it since
55+
// it was created (or last read from or write to storage). If there have been
56+
// no changes, the cache does not need to be written back to storage.
57+
func (dc *DeclCache) Changed() bool {
58+
return dc != nil && dc.changed
59+
}
5060

51-
fmt.Printf(">>> GetDecl(%q): Miss\n", fullname) // TODO(grantnelson-wf): REMOVE
52-
return nil
61+
// HasCache indicates that this cache contains at least one declaration in it.
62+
func (dc *DeclCache) HasCache() bool {
63+
return dc.count() > 0
5364
}
5465

55-
func (dc *DeclCache) PutDecl(decl *Decl) {
66+
// count reports how many declarations are currently in the cache.
67+
func (dc *DeclCache) count() int {
5668
if dc == nil {
57-
return // cache is disabled
58-
}
59-
60-
if decl.ForGeneric {
61-
// Do not cache declarations for generic instantiations.
62-
// The type arguments may come from a package depending on this one
63-
// and not on one of this package's dependencies.
64-
//
65-
// If one of this package's dependencies changes, the cache will not be used.
66-
// However, currently, changes to packages depending on this one
67-
// may change and this package's cache may still be used.
68-
// Therefore, if the package depending on this one changes a type from
69-
// being blocking to non-blocking or vice versa, the cached declaration
70-
// may be invalid.
71-
//
72-
// TODO(grantnelson-wf): We could improve this by tracking which packages
73-
// the type arguments come from and allow caching if those are all from
74-
// dependencies. Since we don't use cache when a dependency changes,
75-
// (i.e. has stale cache) we can use generics in that case. We just can't
76-
// cache the instantiations that come from packages depending on this one
77-
// since those don't invalidate dependent packages' caches.
78-
dc.stats.skippedCount++
79-
declCacheGlobalStats.skippedCount++
80-
fmt.Printf(">>> PutDecl(%q): Generic\n", decl.FullName) // TODO(grantnelson-wf): REMOVE
81-
return
82-
}
83-
84-
if !isUniqueDeclFullName(decl.FullName) {
85-
// Only cache declarations with unique names.
86-
dc.stats.skippedCount++
87-
declCacheGlobalStats.skippedCount++
88-
fmt.Printf(">>> PutDecl(%q): Not Unique\n", decl.FullName) // TODO(grantnelson-wf): REMOVE
89-
return
69+
return 0
9070
}
71+
return len(dc.importDecls) + len(dc.typeDecls) + len(dc.varDecls) + len(dc.funcDecls)
72+
}
9173

92-
if existing, ok := dc.decls[decl.FullName]; ok {
93-
if existing != decl {
94-
panic(fmt.Errorf(`decl cache conflict: different decls with same name: %q`, decl.FullName))
95-
}
96-
fmt.Printf(">>> PutDecl(%q): Dup\n", decl.FullName) // TODO(grantnelson-wf): REMOVE
97-
return
74+
func (dc *DeclCache) GetDecls() (importedPaths []string, importDecls, typeDecls, varDecls, funcDecls []*Decl) {
75+
if dc == nil {
76+
return nil, nil, nil, nil, nil // cache is disabled
9877
}
78+
return dc.importedPaths, dc.importDecls, dc.typeDecls, dc.varDecls, dc.funcDecls
79+
}
9980

100-
if dc.decls == nil {
101-
dc.decls = map[string]*Decl{}
81+
func (dc *DeclCache) SetDecls(importedPaths []string, importDecls, typeDecls, varDecls, funcDecls []*Decl) {
82+
if dc == nil {
83+
return // cache is disabled
10284
}
103-
dc.decls[decl.FullName] = decl
85+
dc.importedPaths = importedPaths
86+
dc.importDecls = importDecls
87+
dc.typeDecls = typeDecls
88+
dc.varDecls = varDecls
89+
dc.funcDecls = funcDecls
10490
dc.changed = true
105-
dc.stats.addedCount++
106-
declCacheGlobalStats.addedCount++
91+
}
10792

108-
fmt.Printf(">>> PutDecl(%q): Added\n", decl.FullName) // TODO(grantnelson-wf): REMOVE
93+
type serializableDeclCache struct {
94+
ImportPath string
95+
ImportedPaths []string
96+
ImportDecls []*Decl
97+
TypeDecls []*Decl
98+
VarDecls []*Decl
99+
FuncDecls []*Decl
109100
}
110101

111102
func (dc *DeclCache) Read(decode func(any) error) error {
112103
if dc == nil {
113-
return nil // cache is disabled
104+
return errReadDisabled
114105
}
115106

116-
dc.stats.reads++
117-
declCacheGlobalStats.reads++
118107
start := time.Now()
119-
var count int
120-
if err := decode(&count); err != nil {
121-
return err
122-
}
123108

124-
if dc.decls == nil {
125-
dc.decls = map[string]*Decl{}
109+
ser := serializableDeclCache{}
110+
if err := decode(&ser); err != nil {
111+
return err
126112
}
127-
for i := 0; i < count; i++ {
128-
decl := &Decl{}
129-
if err := decode(decl); err != nil {
130-
return err
131-
}
132-
if decl.ForGeneric || !isUniqueDeclFullName(decl.FullName) {
133-
// These declarations should not have been cached.
134-
continue
135-
}
136-
dc.decls[decl.FullName] = decl
113+
if dc.importPath != ser.ImportPath {
114+
return fmt.Errorf(`read cache for import path %q when wanting %q`, dc.importPath, ser.ImportPath)
137115
}
138116

139-
dc.stats.loadedCount += len(dc.decls)
140-
declCacheGlobalStats.loadedCount += len(dc.decls)
117+
// Only modify the cache after everything has been checked.
118+
dc.importedPaths = ser.ImportedPaths
119+
dc.importDecls = ser.ImportDecls
120+
dc.typeDecls = ser.TypeDecls
121+
dc.varDecls = ser.VarDecls
122+
dc.funcDecls = ser.FuncDecls
123+
dc.changed = false
124+
125+
dc.stats.reads++
126+
declCacheGlobalStats.reads++
127+
128+
declCount := dc.count()
129+
dc.stats.readCount += declCount
130+
declCacheGlobalStats.readCount += declCount
131+
141132
dur := time.Since(start)
142133
dc.stats.readDur += dur
143134
declCacheGlobalStats.readDur += dur
@@ -146,28 +137,31 @@ func (dc *DeclCache) Read(decode func(any) error) error {
146137

147138
func (dc *DeclCache) Write(encode func(any) error) error {
148139
if dc == nil {
149-
return nil // cache is disabled
140+
return errWriteDisabled
150141
}
151142

152-
dc.stats.writes++
153-
declCacheGlobalStats.writes++
154143
start := time.Now()
155-
count := len(dc.decls)
156-
if err := encode(count); err != nil {
144+
145+
ser := serializableDeclCache{
146+
ImportedPaths: dc.importedPaths,
147+
ImportDecls: dc.importDecls,
148+
ImportPath: dc.importPath,
149+
TypeDecls: dc.typeDecls,
150+
VarDecls: dc.varDecls,
151+
FuncDecls: dc.funcDecls,
152+
}
153+
if err := encode(ser); err != nil {
157154
return err
158155
}
156+
dc.changed = false
159157

160-
names := make([]string, 0, count)
161-
for name := range dc.decls {
162-
names = append(names, name)
163-
}
164-
sort.Strings(names)
158+
dc.stats.writes++
159+
declCacheGlobalStats.writes++
160+
161+
declCount := dc.count()
162+
dc.stats.writeCount += declCount
163+
declCacheGlobalStats.writeCount += declCount
165164

166-
for _, name := range names {
167-
if err := encode(dc.decls[name]); err != nil {
168-
return err
169-
}
170-
}
171165
dur := time.Since(start)
172166
dc.stats.writeDur += dur
173167
declCacheGlobalStats.writeDur += dur
@@ -178,18 +172,14 @@ func (dc *DeclCache) String() string {
178172
if dc == nil {
179173
return `[disabled]`
180174
}
181-
return dc.stats.String()
175+
return dc.stats.String() + `, ` + dc.importPath
182176
}
183177

184178
func (s declCacheStats) String() string {
185-
return fmt.Sprintf(`reads=%d, writes=%d, `+
186-
`loaded=%d, added=%d, skipped=%d, used=%d, `+
187-
`readDur=%s, writeDur=%s`,
188-
s.reads, s.writes,
189-
s.loadedCount, s.addedCount, s.skippedCount, s.usedCount,
190-
s.readDur.String(), s.writeDur.String())
179+
return fmt.Sprintf(`reads=%d, writes=%d, readCount=%d, writeCount=%d, readDur=%s, writeDur=%s`,
180+
s.reads, s.writes, s.readCount, s.writeCount, s.readDur.String(), s.writeDur.String())
191181
}
192182

193183
func GlobalDeclCacheStats() string {
194-
return declCacheGlobalStats.String()
184+
return declCacheGlobalStats.String() + `, [global]`
195185
}

0 commit comments

Comments
 (0)