## Compiler {#compiler}
+The build time overhead to building with [Profile Guided Optimization](/doc/pgo) has been reduced significantly.
+Previously, large builds could see 100%+ build time increase from enabling PGO.
+In Go 1.23, overhead should be in the single digit percentages.
+
## Assembler {#assembler}
## Linker {#linker}
return b.build(ctx, a)
}
+// pgoActionID computes the action ID for a preprocess PGO action.
+func (b *Builder) pgoActionID(input string) cache.ActionID {
+ h := cache.NewHash("preprocess PGO profile " + input)
+
+ fmt.Fprintf(h, "preprocess PGO profile\n")
+ fmt.Fprintf(h, "preprofile %s\n", b.toolID("preprofile"))
+ fmt.Fprintf(h, "input %q\n", b.fileHash(input))
+
+ return h.Sum()
+}
+
// pgoActor implements the Actor interface for preprocessing PGO profiles.
type pgoActor struct {
// input is the path to the original pprof profile.
}
func (p *pgoActor) Act(b *Builder, ctx context.Context, a *Action) error {
- // TODO(prattmic): Integrate with build cache to cache output.
+ if b.useCache(a, b.pgoActionID(p.input), a.Target, !b.IsCmdList) || b.IsCmdList {
+ return nil
+ }
+ defer b.flushOutput(a)
sh := b.Shell(a)
return err
}
+ // N.B. Builder.build looks for the out in a.built, regardless of
+ // whether this came from cache.
a.built = a.Target
+
+ if !cfg.BuildN {
+ // Cache the output.
+ //
+ // N.B. We don't use updateBuildID here, as preprocessed PGO profiles
+ // do not contain a build ID. updateBuildID is typically responsible
+ // for adding to the cache, thus we must do so ourselves instead.
+
+ r, err := os.Open(a.Target)
+ if err != nil {
+ return fmt.Errorf("error opening target for caching: %w", err)
+ }
+
+ c := cache.Default()
+ outputID, _, err := c.Put(a.actionID, r)
+ r.Close()
+ if err != nil {
+ return fmt.Errorf("error adding target to cache: %w", err)
+ }
+ if cfg.BuildX {
+ sh.ShowCmd("", "%s # internal", joinUnambiguously(str.StringList("cp", a.Target, c.OutputFile(outputID))))
+ }
+ }
+
return nil
}
// Check to see if the action output is cached.
if file, _, err := cache.GetFile(c, actionHash); err == nil {
+ if a.Mode == "preprocess PGO profile" {
+ // Preprocessed PGO profiles don't embed a build ID, so
+ // skip the build ID lookup.
+ // TODO(prattmic): better would be to add a build ID to the format.
+ a.built = file
+ a.Target = "DO NOT USE - using cache"
+ return true
+ }
if buildID, err := buildid.ReadFile(file); err == nil {
if printOutput {
showStdout(b, c, a, "stdout")
--- /dev/null
+[short] skip
+
+# Set up fresh GOCACHE.
+env GOCACHE=$WORK/gocache
+mkdir $GOCACHE
+
+# Building trivial non-main package should run preprofile the first time.
+go build -x -pgo=default.pgo lib.go
+stderr 'preprofile.*default\.pgo'
+
+# ... but not again ...
+go build -x -pgo=default.pgo lib.go
+! stderr 'preprofile.*default\.pgo'
+
+# ... unless we use -a.
+go build -a -x -pgo=default.pgo lib.go
+stderr 'preprofile.*default\.pgo'
+
+# ... building a different package should not run preprofile again, instead using a profile from cache.
+go build -x -pgo=default.pgo lib2.go
+! stderr 'preprofile.*default\.pgo'
+stderr 'compile.*-pgoprofile=.*'$GOCACHE'.*lib2.go'
+
+-- lib.go --
+package lib
+-- lib2.go --
+package lib2
+-- default.pgo --
[short] skip 'compiles and links executables'
+# Set up fresh GOCACHE.
+env GOCACHE=$WORK/gocache
+mkdir $GOCACHE
+
# build without PGO
go build triv.go
[short] skip 'compiles and links executables'
+# Set up fresh GOCACHE.
+env GOCACHE=$WORK/gocache
+mkdir $GOCACHE
+
# use default.pgo for a single main package
go build -n -pgo=auto -o a1.exe ./a/a1
stderr 'preprofile.*-i.*default\.pgo'
import (
"bufio"
+ "cmd/internal/objabi"
"cmd/internal/pgo"
"flag"
"fmt"
}
func main() {
+ objabi.AddVersionFlag()
+
log.SetFlags(0)
log.SetPrefix("preprofile: ")