context: Allow running some singletons in parallel.
Many of the singletons are trivial and can be run in parallel, improving the performance during analysis. Bug: 281536768 Test: manual, presubmit Change-Id: Ia63e4bc42a68e65dfa800e770982fa5826355fad
This commit is contained in:
parent
ff04c33f2a
commit
12ccb17d4e
4 changed files with 126 additions and 44 deletions
|
@ -91,7 +91,7 @@ func RunBlueprint(args Args, stopBefore StopBefore, ctx *blueprint.Context, conf
|
||||||
ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)
|
ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)
|
||||||
ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory())
|
ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory())
|
||||||
ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory())
|
ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory())
|
||||||
ctx.RegisterSingletonType("bootstrap", newSingletonFactory())
|
ctx.RegisterSingletonType("bootstrap", newSingletonFactory(), false)
|
||||||
blueprint.RegisterPackageIncludesModuleType(ctx)
|
blueprint.RegisterPackageIncludesModuleType(ctx)
|
||||||
|
|
||||||
ctx.BeginEvent("parse_bp")
|
ctx.BeginEvent("parse_bp")
|
||||||
|
|
|
@ -227,7 +227,7 @@ func generateGlobNinjaFile(glob *GlobSingleton, config interface{}) ([]byte, []e
|
||||||
ctx := blueprint.NewContext()
|
ctx := blueprint.NewContext()
|
||||||
ctx.RegisterSingletonType("glob", func() blueprint.Singleton {
|
ctx.RegisterSingletonType("glob", func() blueprint.Singleton {
|
||||||
return glob
|
return glob
|
||||||
})
|
}, false)
|
||||||
|
|
||||||
extraDeps, errs := ctx.ResolveDependencies(config)
|
extraDeps, errs := ctx.ResolveDependencies(config)
|
||||||
if len(extraDeps) > 0 {
|
if len(extraDeps) > 0 {
|
||||||
|
|
102
context.go
102
context.go
|
@ -441,6 +441,7 @@ type singletonInfo struct {
|
||||||
factory SingletonFactory
|
factory SingletonFactory
|
||||||
singleton Singleton
|
singleton Singleton
|
||||||
name string
|
name string
|
||||||
|
parallel bool
|
||||||
|
|
||||||
// set during PrepareBuildActions
|
// set during PrepareBuildActions
|
||||||
actionDefs localBuildActions
|
actionDefs localBuildActions
|
||||||
|
@ -566,13 +567,15 @@ type SingletonFactory func() Singleton
|
||||||
|
|
||||||
// RegisterSingletonType registers a singleton type that will be invoked to
|
// RegisterSingletonType registers a singleton type that will be invoked to
|
||||||
// generate build actions. Each registered singleton type is instantiated
|
// generate build actions. Each registered singleton type is instantiated
|
||||||
// and invoked exactly once as part of the generate phase. Each registered
|
// and invoked exactly once as part of the generate phase.
|
||||||
// singleton is invoked in registration order.
|
//
|
||||||
|
// Those singletons registered with parallel=true are run in parallel, after
|
||||||
|
// which the other registered singletons are run in registration order.
|
||||||
//
|
//
|
||||||
// The singleton type names given here must be unique for the context. The
|
// The singleton type names given here must be unique for the context. The
|
||||||
// factory function should be a named function so that its package and name can
|
// factory function should be a named function so that its package and name can
|
||||||
// be included in the generated Ninja file for debugging purposes.
|
// be included in the generated Ninja file for debugging purposes.
|
||||||
func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) {
|
func (c *Context) RegisterSingletonType(name string, factory SingletonFactory, parallel bool) {
|
||||||
for _, s := range c.singletonInfo {
|
for _, s := range c.singletonInfo {
|
||||||
if s.name == name {
|
if s.name == name {
|
||||||
panic(fmt.Errorf("singleton %q is already registered", name))
|
panic(fmt.Errorf("singleton %q is already registered", name))
|
||||||
|
@ -583,6 +586,7 @@ func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) {
|
||||||
factory: factory,
|
factory: factory,
|
||||||
singleton: factory(),
|
singleton: factory(),
|
||||||
name: name,
|
name: name,
|
||||||
|
parallel: parallel,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,6 +609,7 @@ func (c *Context) RegisterPreSingletonType(name string, factory SingletonFactory
|
||||||
factory: factory,
|
factory: factory,
|
||||||
singleton: factory(),
|
singleton: factory(),
|
||||||
name: name,
|
name: name,
|
||||||
|
parallel: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3314,6 +3319,8 @@ func spliceModules(modules modulesOrAliases, i int, newModules modulesOrAliases)
|
||||||
func (c *Context) generateModuleBuildActions(config interface{},
|
func (c *Context) generateModuleBuildActions(config interface{},
|
||||||
liveGlobals *liveTracker) ([]string, []error) {
|
liveGlobals *liveTracker) ([]string, []error) {
|
||||||
|
|
||||||
|
c.BeginEvent("generateModuleBuildActions")
|
||||||
|
defer c.EndEvent("generateModuleBuildActions")
|
||||||
var deps []string
|
var deps []string
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
|
@ -3411,13 +3418,12 @@ func (c *Context) generateModuleBuildActions(config interface{},
|
||||||
return deps, errs
|
return deps, errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) generateSingletonBuildActions(config interface{},
|
func (c *Context) generateOneSingletonBuildActions(config interface{},
|
||||||
singletons []*singletonInfo, liveGlobals *liveTracker) ([]string, []error) {
|
info *singletonInfo, liveGlobals *liveTracker) ([]string, []error) {
|
||||||
|
|
||||||
var deps []string
|
var deps []string
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, info := range singletons {
|
|
||||||
// The parent scope of the singletonContext's local scope gets overridden to be that of the
|
// The parent scope of the singletonContext's local scope gets overridden to be that of the
|
||||||
// calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
|
// calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
|
||||||
// just set it to nil.
|
// just set it to nil.
|
||||||
|
@ -3448,10 +3454,7 @@ func (c *Context) generateSingletonBuildActions(config interface{},
|
||||||
|
|
||||||
if len(sctx.errs) > 0 {
|
if len(sctx.errs) > 0 {
|
||||||
errs = append(errs, sctx.errs...)
|
errs = append(errs, sctx.errs...)
|
||||||
if len(errs) > maxErrors {
|
return deps, errs
|
||||||
break
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deps = append(deps, sctx.ninjaFileDeps...)
|
deps = append(deps, sctx.ninjaFileDeps...)
|
||||||
|
@ -3459,10 +3462,89 @@ func (c *Context) generateSingletonBuildActions(config interface{},
|
||||||
newErrs := c.processLocalBuildActions(&info.actionDefs,
|
newErrs := c.processLocalBuildActions(&info.actionDefs,
|
||||||
&sctx.actionDefs, liveGlobals)
|
&sctx.actionDefs, liveGlobals)
|
||||||
errs = append(errs, newErrs...)
|
errs = append(errs, newErrs...)
|
||||||
|
return deps, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) generateParallelSingletonBuildActions(config interface{},
|
||||||
|
singletons []*singletonInfo, liveGlobals *liveTracker) ([]string, []error) {
|
||||||
|
|
||||||
|
c.BeginEvent("generateParallelSingletonBuildActions")
|
||||||
|
defer c.EndEvent("generateParallelSingletonBuildActions")
|
||||||
|
|
||||||
|
var deps []string
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
cancelCh := make(chan struct{})
|
||||||
|
depsCh := make(chan []string)
|
||||||
|
errsCh := make(chan []error)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-cancelCh:
|
||||||
|
close(cancelCh)
|
||||||
|
return
|
||||||
|
case dep := <-depsCh:
|
||||||
|
deps = append(deps, dep...)
|
||||||
|
case newErrs := <-errsCh:
|
||||||
|
if len(errs) <= maxErrors {
|
||||||
|
errs = append(errs, newErrs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for _, info := range singletons {
|
||||||
|
if !info.parallel {
|
||||||
|
// Skip any singletons registered with parallel=false.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func(inf *singletonInfo) {
|
||||||
|
defer wg.Done()
|
||||||
|
newDeps, newErrs := c.generateOneSingletonBuildActions(config, inf, liveGlobals)
|
||||||
|
depsCh <- newDeps
|
||||||
|
errsCh <- newErrs
|
||||||
|
}(info)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
cancelCh <- struct{}{}
|
||||||
|
<-cancelCh
|
||||||
|
|
||||||
|
return deps, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) generateSingletonBuildActions(config interface{},
|
||||||
|
singletons []*singletonInfo, liveGlobals *liveTracker) ([]string, []error) {
|
||||||
|
|
||||||
|
c.BeginEvent("generateSingletonBuildActions")
|
||||||
|
defer c.EndEvent("generateSingletonBuildActions")
|
||||||
|
|
||||||
|
var deps []string
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
// Run one singleton. Use a variable to simplify manual validation testing.
|
||||||
|
var runSingleton = func(info *singletonInfo) {
|
||||||
|
c.BeginEvent("singleton:" + info.name)
|
||||||
|
defer c.EndEvent("singleton:" + info.name)
|
||||||
|
newDeps, newErrs := c.generateOneSingletonBuildActions(config, info, liveGlobals)
|
||||||
|
deps = append(deps, newDeps...)
|
||||||
|
errs = append(errs, newErrs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, take care of any singletons that want to run in parallel.
|
||||||
|
deps, errs = c.generateParallelSingletonBuildActions(config, singletons, liveGlobals)
|
||||||
|
|
||||||
|
for _, info := range singletons {
|
||||||
|
if !info.parallel {
|
||||||
|
runSingleton(info)
|
||||||
if len(errs) > maxErrors {
|
if len(errs) > maxErrors {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return deps, errs
|
return deps, errs
|
||||||
}
|
}
|
||||||
|
|
|
@ -590,7 +590,7 @@ func TestAddNinjaFileDeps(t *testing.T) {
|
||||||
ctx.RegisterBottomUpMutator("testBottomUpMutator", addNinjaDepsTestBottomUpMutator)
|
ctx.RegisterBottomUpMutator("testBottomUpMutator", addNinjaDepsTestBottomUpMutator)
|
||||||
ctx.RegisterTopDownMutator("testTopDownMutator", addNinjaDepsTestTopDownMutator)
|
ctx.RegisterTopDownMutator("testTopDownMutator", addNinjaDepsTestTopDownMutator)
|
||||||
ctx.RegisterPreSingletonType("testPreSingleton", addNinjaDepsTestPreSingletonFactory)
|
ctx.RegisterPreSingletonType("testPreSingleton", addNinjaDepsTestPreSingletonFactory)
|
||||||
ctx.RegisterSingletonType("testSingleton", addNinjaDepsTestSingletonFactory)
|
ctx.RegisterSingletonType("testSingleton", addNinjaDepsTestSingletonFactory, false)
|
||||||
parseDeps, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
|
parseDeps, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
t.Errorf("unexpected parse errors:")
|
t.Errorf("unexpected parse errors:")
|
||||||
|
|
Loading…
Reference in a new issue