Skip to content

Commit d0f6d18

Browse files
feat: Reduce bob graph generation overhead
Cache runtime feature/property reflection types used during graph generation and replace linear transitive metadata dedupe with a map-backed set. On a fresh codex-bob-trace-final build, build.ninja generation dropped from about 50s baseline to about 28s before target compilation. Test: GOCACHE=/tmp/codex-go-cache GOPATH=/tmp/codex-go-path go test ./core/... (cherry picked from commit 9449984d049957049c87c9d4821e17b70bbfbdce) Change-Id: I54777d8ac2000c0356a80fdf053b74c6c213f099
1 parent 47b1b5a commit d0f6d18

2 files changed

Lines changed: 92 additions & 13 deletions

File tree

‎core/feature.go‎

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,79 @@ package core
22

33
import (
44
"reflect"
5+
"strconv"
56
"strings"
7+
"sync"
68

79
"github.com/ARM-software/bob-build/core/config"
810
"github.com/ARM-software/bob-build/internal/utils"
911
)
1012

13+
var featureTypeCache = struct {
14+
sync.RWMutex
15+
coalesced map[string]reflect.Type
16+
features map[featureStructTypeKey]reflect.Type
17+
}{
18+
coalesced: make(map[string]reflect.Type),
19+
features: make(map[featureStructTypeKey]reflect.Type),
20+
}
21+
22+
type featureStructTypeKey struct {
23+
featureList string
24+
propsType reflect.Type
25+
}
26+
27+
func reflectTypeCacheKey(t reflect.Type) string {
28+
return t.PkgPath() + "\x00" + t.String()
29+
}
30+
31+
func reflectTypeListCacheKey(list []reflect.Type) string {
32+
var builder strings.Builder
33+
for _, typ := range list {
34+
key := reflectTypeCacheKey(typ)
35+
builder.WriteString(strconv.Itoa(len(key)))
36+
builder.WriteByte(':')
37+
builder.WriteString(key)
38+
}
39+
return builder.String()
40+
}
41+
42+
func featureListCacheKey(features []string) string {
43+
return strings.Join(features, "\x00")
44+
}
45+
46+
func featureStructType(properties *config.Properties, propsType reflect.Type) reflect.Type {
47+
key := featureStructTypeKey{
48+
featureList: featureListCacheKey(properties.FeatureList),
49+
propsType: propsType,
50+
}
51+
52+
featureTypeCache.RLock()
53+
cached, ok := featureTypeCache.features[key]
54+
featureTypeCache.RUnlock()
55+
if ok {
56+
return cached
57+
}
58+
59+
featureTypeCache.Lock()
60+
defer featureTypeCache.Unlock()
61+
if cached, ok := featureTypeCache.features[key]; ok {
62+
return cached
63+
}
64+
65+
fields := make([]reflect.StructField, len(properties.FeatureList))
66+
for i, featureName := range properties.FeatureList {
67+
fields[i] = reflect.StructField{
68+
Name: featurePropertyName(featureName),
69+
Type: reflect.PtrTo(propsType),
70+
}
71+
}
72+
73+
cached = reflect.StructOf(fields)
74+
featureTypeCache.features[key] = cached
75+
return cached
76+
}
77+
1178
// featurePropertyName returns name of feature. Name needs to start from capital letter because
1279
// this is how it works in go exported/unexported properties
1380
// e.g. Android, Foo_bar
@@ -62,17 +129,7 @@ func (f *Features) Init(properties *config.Properties, list ...interface{}) {
62129
}
63130

64131
propsType := coalesceTypes(typesOf(list...)...)
65-
fields := make([]reflect.StructField, len(properties.FeatureList))
66-
67-
for i, featureName := range properties.FeatureList {
68-
fields[i] = reflect.StructField{
69-
Name: featurePropertyName(featureName),
70-
Type: reflect.PtrTo(propsType),
71-
}
72-
}
73-
74-
bpFeatureStruct := reflect.StructOf(fields)
75-
instancePtr := reflect.New(bpFeatureStruct)
132+
instancePtr := reflect.New(featureStructType(properties, propsType))
76133
f.BlueprintEmbed = instancePtr.Interface()
77134

78135
}
@@ -130,6 +187,21 @@ func coalesceTypes(list ...reflect.Type) reflect.Type {
130187
return list[0]
131188
}
132189

190+
key := reflectTypeListCacheKey(list)
191+
192+
featureTypeCache.RLock()
193+
cached, ok := featureTypeCache.coalesced[key]
194+
featureTypeCache.RUnlock()
195+
if ok {
196+
return cached
197+
}
198+
199+
featureTypeCache.Lock()
200+
defer featureTypeCache.Unlock()
201+
if cached, ok := featureTypeCache.coalesced[key]; ok {
202+
return cached
203+
}
204+
133205
fieldsKeys := map[string]bool{}
134206
fields := []reflect.StructField{}
135207

@@ -146,7 +218,9 @@ func coalesceTypes(list ...reflect.Type) reflect.Type {
146218
}
147219
}
148220

149-
return reflect.StructOf(fields)
221+
cached = reflect.StructOf(fields)
222+
featureTypeCache.coalesced[key] = cached
223+
return cached
150224
}
151225

152226
// AppendProps merges properties from BlueprintEmbed to dst, but only for enabled features

‎core/metadata.go‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,13 @@ func metaDataCollector(ctx blueprint.BottomUpMutatorContext) {
4949
})
5050
}
5151

52+
seenDeps := map[string]bool{}
5253
ctx.WalkDeps(func(dep, parent blueprint.Module) bool {
53-
meta.TransitiveDeps = utils.AppendIfUnique(meta.TransitiveDeps, dep.Name())
54+
name := dep.Name()
55+
if !seenDeps[name] {
56+
seenDeps[name] = true
57+
meta.TransitiveDeps = append(meta.TransitiveDeps, name)
58+
}
5459
return true
5560
})
5661

0 commit comments

Comments
 (0)