diff options
| author | Colin Cross <ccross@android.com> | 2021-04-05 17:20:34 -0700 |
|---|---|---|
| committer | Colin Cross <ccross@android.com> | 2021-04-15 11:04:11 -0700 |
| commit | 2523698c12a78fb6b860ce6da836bafad4409b30 (patch) | |
| tree | 3d4f2ae9a2a6e753bd5c20839d203df40d46c844 /glob.go | |
| parent | 45222ec3ca6e7117aac2b10795c5ee37f62188f5 (diff) | |
| download | platform_build_blueprint-2523698c12a78fb6b860ce6da836bafad4409b30.tar.gz platform_build_blueprint-2523698c12a78fb6b860ce6da836bafad4409b30.tar.bz2 platform_build_blueprint-2523698c12a78fb6b860ce6da836bafad4409b30.zip | |
Speed up globs with sharding
There are a few cases that force all globs to be rerun at the beginning
of the build (changes to bpglob or dependencies, second build after a
clean build). The number of globs has gotten high enough that rerunning
them all can have significant overhead to start bpglob for each one.
Replace the per-glob bpglob invocations with sharded invocations using
1024 hash buckets.
Bug: 159845846
Test: glob_test.go
Test: m nothing && m nothing
Test: build/soong/bootstrap_test.sh
Change-Id: Ife1f7a03c8f6b25d1be01531425d8dc2c76d1ea0
Diffstat (limited to 'glob.go')
| -rw-r--r-- | glob.go | 93 |
1 files changed, 37 insertions, 56 deletions
@@ -15,50 +15,44 @@ package blueprint import ( - "crypto/md5" "fmt" - "path/filepath" "sort" "strings" "github.com/google/blueprint/pathtools" ) -type GlobPath struct { - pathtools.GlobResult - Name string -} - -func (g *GlobPath) FileListFile(buildDir string) string { - return filepath.Join(buildDir, ".glob", g.Name) -} - -func verifyGlob(fileName, pattern string, excludes []string, g GlobPath) { +func verifyGlob(key globKey, pattern string, excludes []string, g pathtools.GlobResult) { if pattern != g.Pattern { - panic(fmt.Errorf("Mismatched patterns %q and %q for glob file %q", pattern, g.Pattern, fileName)) + panic(fmt.Errorf("Mismatched patterns %q and %q for glob key %q", pattern, g.Pattern, key)) } if len(excludes) != len(g.Excludes) { - panic(fmt.Errorf("Mismatched excludes %v and %v for glob file %q", excludes, g.Excludes, fileName)) + panic(fmt.Errorf("Mismatched excludes %v and %v for glob key %q", excludes, g.Excludes, key)) } for i := range excludes { if g.Excludes[i] != excludes[i] { - panic(fmt.Errorf("Mismatched excludes %v and %v for glob file %q", excludes, g.Excludes, fileName)) + panic(fmt.Errorf("Mismatched excludes %v and %v for glob key %q", excludes, g.Excludes, key)) } } } func (c *Context) glob(pattern string, excludes []string) ([]string, error) { - fileName := globToFileName(pattern, excludes) + // Sort excludes so that two globs with the same excludes in a different order reuse the same + // key. Make a copy first to avoid modifying the caller's version. + excludes = append([]string(nil), excludes...) + sort.Strings(excludes) + + key := globToKey(pattern, excludes) // Try to get existing glob from the stored results c.globLock.Lock() - g, exists := c.globs[fileName] + g, exists := c.globs[key] c.globLock.Unlock() if exists { // Glob has already been done, double check it is identical - verifyGlob(fileName, pattern, excludes, g) + verifyGlob(key, pattern, excludes, g) // Return a copy so that modifications don't affect the cached value. return append([]string(nil), g.Matches...), nil } @@ -71,14 +65,14 @@ func (c *Context) glob(pattern string, excludes []string) ([]string, error) { // Store the results c.globLock.Lock() - if g, exists = c.globs[fileName]; !exists { - c.globs[fileName] = GlobPath{result, fileName} + if g, exists = c.globs[key]; !exists { + c.globs[key] = result } c.globLock.Unlock() if exists { // Getting the list raced with another goroutine, throw away the results and use theirs - verifyGlob(fileName, pattern, excludes, g) + verifyGlob(key, pattern, excludes, g) // Return a copy so that modifications don't affect the cached value. return append([]string(nil), g.Matches...), nil } @@ -87,49 +81,36 @@ func (c *Context) glob(pattern string, excludes []string) ([]string, error) { return append([]string(nil), result.Matches...), nil } -func (c *Context) Globs() []GlobPath { - fileNames := make([]string, 0, len(c.globs)) +func (c *Context) Globs() pathtools.MultipleGlobResults { + keys := make([]globKey, 0, len(c.globs)) for k := range c.globs { - fileNames = append(fileNames, k) + keys = append(keys, k) } - sort.Strings(fileNames) - globs := make([]GlobPath, len(fileNames)) - for i, fileName := range fileNames { - globs[i] = c.globs[fileName] + sort.Slice(keys, func(i, j int) bool { + if keys[i].pattern != keys[j].pattern { + return keys[i].pattern < keys[j].pattern + } + return keys[i].excludes < keys[j].excludes + }) + + globs := make(pathtools.MultipleGlobResults, len(keys)) + for i, key := range keys { + globs[i] = c.globs[key] } return globs } -func globToString(pattern string) string { - ret := "" - for _, c := range pattern { - switch { - case c >= 'a' && c <= 'z', - c >= 'A' && c <= 'Z', - c >= '0' && c <= '9', - c == '_', c == '-', c == '/': - ret += string(c) - default: - ret += "_" - } - } - - return ret +// globKey combines a pattern and a list of excludes into a hashable struct to be used as a key in +// a map. +type globKey struct { + pattern string + excludes string } -func globToFileName(pattern string, excludes []string) string { - name := globToString(pattern) - excludeName := "" - for _, e := range excludes { - excludeName += "__" + globToString(e) - } - - // Prevent file names from reaching ninja's path component limit - if strings.Count(name, "/")+strings.Count(excludeName, "/") > 30 { - excludeName = fmt.Sprintf("___%x", md5.Sum([]byte(excludeName))) - } - - return name + excludeName + ".glob" +// globToKey converts a pattern and an excludes list into a globKey struct that is hashable and +// usable as a key in a map. +func globToKey(pattern string, excludes []string) globKey { + return globKey{pattern, strings.Join(excludes, "|")} } |
