]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go/internal/work: use par.Cache to cache tool IDs.
authorqmuntal <quimmuntal@gmail.com>
Fri, 28 Feb 2025 19:21:44 +0000 (20:21 +0100)
committerQuim Muntal <quimmuntal@gmail.com>
Sat, 1 Mar 2025 08:29:04 +0000 (00:29 -0800)
The tool IDs can be calculated once and reused across multiple
threads. This is a small optimization that helps optimize system
resources.

On a normal Windows machine with 12 virtual CPUs, the time to build
a hello world program is reduced from over 1 second, with spikes of 2
seconds, to a consistent 0.7 seconds.

Updates #71981.

Change-Id: I85f4a19f8ad4230afa32213780c761b7eb22fa29
Reviewed-on: https://go-review.googlesource.com/c/go/+/653715
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/go/internal/work/action.go
src/cmd/go/internal/work/buildid.go

index 44bb9f8c1e4a0529ca335d810fc845c78c06609d..2426720021007543bfc81f4325848e8bef6bdc6b 100644 (file)
@@ -10,6 +10,7 @@ import (
        "bufio"
        "bytes"
        "cmd/internal/cov/covcmd"
+       "cmd/internal/par"
        "container/heap"
        "context"
        "debug/elf"
@@ -56,9 +57,10 @@ type Builder struct {
        readySema chan bool
        ready     actionQueue
 
-       id           sync.Mutex
-       toolIDCache  map[string]string // tool name -> tool ID
-       buildIDCache map[string]string // file name -> build ID
+       id             sync.Mutex
+       toolIDCache    par.Cache[string, string] // tool name -> tool ID
+       gccToolIDCache map[string]string         // tool name -> tool ID
+       buildIDCache   map[string]string         // file name -> build ID
 }
 
 // NOTE: Much of Action would not need to be exported if not for test.
@@ -268,7 +270,7 @@ func NewBuilder(workDir string) *Builder {
        b := new(Builder)
 
        b.actionCache = make(map[cacheKey]*Action)
-       b.toolIDCache = make(map[string]string)
+       b.gccToolIDCache = make(map[string]string)
        b.buildIDCache = make(map[string]string)
 
        printWorkDir := false
index cab722c28a8396f4bad9a96a6193616b31d23cd6..3497ad7a27dc3f3d56d420ba5b610ebe3c599858 100644 (file)
@@ -144,55 +144,42 @@ func contentID(buildID string) string {
 // build setups agree on details like $GOROOT and file name paths, but at least the
 // tool IDs do not make it impossible.)
 func (b *Builder) toolID(name string) string {
-       b.id.Lock()
-       id := b.toolIDCache[name]
-       b.id.Unlock()
-
-       if id != "" {
-               return id
-       }
-
-       path := base.Tool(name)
-       desc := "go tool " + name
-
-       // Special case: undocumented -vettool overrides usual vet,
-       // for testing vet or supplying an alternative analysis tool.
-       if name == "vet" && VetTool != "" {
-               path = VetTool
-               desc = VetTool
-       }
+       return b.toolIDCache.Do(name, func() string {
+               path := base.Tool(name)
+               desc := "go tool " + name
+
+               // Special case: undocumented -vettool overrides usual vet,
+               // for testing vet or supplying an alternative analysis tool.
+               if name == "vet" && VetTool != "" {
+                       path = VetTool
+                       desc = VetTool
+               }
 
-       cmdline := str.StringList(cfg.BuildToolexec, path, "-V=full")
-       cmd := exec.Command(cmdline[0], cmdline[1:]...)
-       var stdout, stderr strings.Builder
-       cmd.Stdout = &stdout
-       cmd.Stderr = &stderr
-       if err := cmd.Run(); err != nil {
-               if stderr.Len() > 0 {
-                       os.Stderr.WriteString(stderr.String())
+               cmdline := str.StringList(cfg.BuildToolexec, path, "-V=full")
+               cmd := exec.Command(cmdline[0], cmdline[1:]...)
+               var stdout, stderr strings.Builder
+               cmd.Stdout = &stdout
+               cmd.Stderr = &stderr
+               if err := cmd.Run(); err != nil {
+                       if stderr.Len() > 0 {
+                               os.Stderr.WriteString(stderr.String())
+                       }
+                       base.Fatalf("go: error obtaining buildID for %s: %v", desc, err)
                }
-               base.Fatalf("go: error obtaining buildID for %s: %v", desc, err)
-       }
 
-       line := stdout.String()
-       f := strings.Fields(line)
-       if len(f) < 3 || f[0] != name && path != VetTool || f[1] != "version" || f[2] == "devel" && !strings.HasPrefix(f[len(f)-1], "buildID=") {
-               base.Fatalf("go: parsing buildID from %s -V=full: unexpected output:\n\t%s", desc, line)
-       }
-       if f[2] == "devel" {
-               // On the development branch, use the content ID part of the build ID.
-               id = contentID(f[len(f)-1])
-       } else {
+               line := stdout.String()
+               f := strings.Fields(line)
+               if len(f) < 3 || f[0] != name && path != VetTool || f[1] != "version" || f[2] == "devel" && !strings.HasPrefix(f[len(f)-1], "buildID=") {
+                       base.Fatalf("go: parsing buildID from %s -V=full: unexpected output:\n\t%s", desc, line)
+               }
+               if f[2] == "devel" {
+                       // On the development branch, use the content ID part of the build ID.
+                       return contentID(f[len(f)-1])
+               }
                // For a release, the output is like: "compile version go1.9.1 X:framepointer".
                // Use the whole line.
-               id = strings.TrimSpace(line)
-       }
-
-       b.id.Lock()
-       b.toolIDCache[name] = id
-       b.id.Unlock()
-
-       return id
+               return strings.TrimSpace(line)
+       })
 }
 
 // gccToolID returns the unique ID to use for a tool that is invoked
@@ -216,10 +203,11 @@ func (b *Builder) toolID(name string) string {
 // to detect changes in the underlying compiler. The returned exe can be empty,
 // which means to rely only on the id.
 func (b *Builder) gccToolID(name, language string) (id, exe string, err error) {
+       //TODO: Use par.Cache instead of a mutex and a map. See Builder.toolID.
        key := name + "." + language
        b.id.Lock()
-       id = b.toolIDCache[key]
-       exe = b.toolIDCache[key+".exe"]
+       id = b.gccToolIDCache[key]
+       exe = b.gccToolIDCache[key+".exe"]
        b.id.Unlock()
 
        if id != "" {
@@ -309,8 +297,8 @@ func (b *Builder) gccToolID(name, language string) (id, exe string, err error) {
        }
 
        b.id.Lock()
-       b.toolIDCache[key] = id
-       b.toolIDCache[key+".exe"] = exe
+       b.gccToolIDCache[key] = id
+       b.gccToolIDCache[key+".exe"] = exe
        b.id.Unlock()
 
        return id, exe, nil