Skip to content

Commit 9819d82

Browse files
prattmicgopherbot
authored andcommitted
cmd/compile: avoid reading entire PGO profile just to check the header
isPreProfileFile reads the entire file into memory just to check the first few bytes, and then throws it all away. We can avoid this by just peeking at the beginning. For #58102. Change-Id: Id2c2844e5e44a2f3a9c7cdb9a027d94d26bdf71d Reviewed-on: https://go-review.googlesource.com/c/go/+/560035 Reviewed-by: Cherry Mui <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Michael Pratt <[email protected]>
1 parent 3d20a32 commit 9819d82

File tree

1 file changed

+33
-38
lines changed

1 file changed

+33
-38
lines changed

src/cmd/compile/internal/pgo/irgraph.go

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import (
4949
"errors"
5050
"fmt"
5151
"internal/profile"
52-
"io/ioutil"
52+
"io"
5353
"os"
5454
"sort"
5555
"strconv"
@@ -145,51 +145,52 @@ type Profile struct {
145145

146146
var wantHdr = "GO PREPROFILE V1\n"
147147

148-
func isPreProfileFile(filename string) (bool, error) {
149-
content, err := ioutil.ReadFile(filename)
150-
if err != nil {
151-
return false, err
148+
func isPreProfileFile(r *bufio.Reader) (bool, error) {
149+
hdr, err := r.Peek(len(wantHdr))
150+
if err == io.EOF {
151+
// Empty file.
152+
return false, nil
153+
} else if err != nil {
154+
return false, fmt.Errorf("error reading profile header: %w", err)
152155
}
153156

154-
/* check the header */
155-
fileContent := string(content)
156-
if strings.HasPrefix(fileContent, wantHdr) {
157-
return true, nil
158-
}
159-
return false, nil
157+
return string(hdr) == wantHdr, nil
160158
}
161159

162160
// New generates a profile-graph from the profile or pre-processed profile.
163161
func New(profileFile string) (*Profile, error) {
164-
var profile *Profile
165-
var err error
166-
isPreProf, err := isPreProfileFile(profileFile)
162+
f, err := os.Open(profileFile)
167163
if err != nil {
168164
return nil, fmt.Errorf("error opening profile: %w", err)
169165
}
170-
if !isPreProf {
171-
profile, err = processProto(profileFile)
172-
if err != nil {
173-
return nil, fmt.Errorf("error processing pprof PGO profile: %w", err)
174-
}
175-
} else {
176-
profile, err = processPreprof(profileFile)
166+
defer f.Close()
167+
168+
r := bufio.NewReader(f)
169+
170+
isPreProf, err := isPreProfileFile(r)
171+
if err != nil {
172+
return nil, fmt.Errorf("error processing profile header: %w", err)
173+
}
174+
175+
if isPreProf {
176+
profile, err := processPreprof(r)
177177
if err != nil {
178178
return nil, fmt.Errorf("error processing preprocessed PGO profile: %w", err)
179179
}
180+
return profile, nil
181+
}
182+
183+
profile, err := processProto(r)
184+
if err != nil {
185+
return nil, fmt.Errorf("error processing pprof PGO profile: %w", err)
180186
}
181187
return profile, nil
182188

183189
}
184190

185191
// processProto generates a profile-graph from the profile.
186-
func processProto(profileFile string) (*Profile, error) {
187-
f, err := os.Open(profileFile)
188-
if err != nil {
189-
return nil, fmt.Errorf("error opening profile: %w", err)
190-
}
191-
defer f.Close()
192-
p, err := profile.Parse(f)
192+
func processProto(r io.Reader) (*Profile, error) {
193+
p, err := profile.Parse(r)
193194
if errors.Is(err, profile.ErrNoData) {
194195
// Treat a completely empty file the same as a profile with no
195196
// samples: nothing to do.
@@ -242,8 +243,8 @@ func processProto(profileFile string) (*Profile, error) {
242243
}
243244

244245
// processPreprof generates a profile-graph from the pre-procesed profile.
245-
func processPreprof(preprofileFile string) (*Profile, error) {
246-
namedEdgeMap, totalWeight, err := createNamedEdgeMapFromPreprocess(preprofileFile)
246+
func processPreprof(r io.Reader) (*Profile, error) {
247+
namedEdgeMap, totalWeight, err := createNamedEdgeMapFromPreprocess(r)
247248
if err != nil {
248249
return nil, err
249250
}
@@ -297,14 +298,8 @@ func postProcessNamedEdgeMap(weight map[NamedCallEdge]int64, weightVal int64) (e
297298

298299
// restore NodeMap information from a preprocessed profile.
299300
// The reader can refer to the format of preprocessed profile in cmd/preprofile/main.go.
300-
func createNamedEdgeMapFromPreprocess(preprofileFile string) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
301-
readFile, err := os.Open(preprofileFile)
302-
if err != nil {
303-
return NamedEdgeMap{}, 0, fmt.Errorf("error opening preprocessed profile: %w", err)
304-
}
305-
defer readFile.Close()
306-
307-
fileScanner := bufio.NewScanner(readFile)
301+
func createNamedEdgeMapFromPreprocess(r io.Reader) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
302+
fileScanner := bufio.NewScanner(r)
308303
fileScanner.Split(bufio.ScanLines)
309304
weight := make(map[NamedCallEdge]int64)
310305

0 commit comments

Comments
 (0)