cmd/guru: re-use buffer for reading files
I felt guilty about leaving 2% on the table in CL 108878, so I thought I'd get it a different way. Teach readFile to accept a re-usable bytes.Buffer to read into, to reduce the amount of garbage created. To limit the possible memory impact of giant files, only re-use the buffer for the duration of a single package. Even that is enough to help. name old time/op new time/op delta Referrers 4.67s ± 3% 4.58s ± 2% -1.96% (p=0.029 n=10+10) name old user-time/op new user-time/op delta Referrers 16.5s ± 3% 15.8s ± 1% -4.39% (p=0.000 n=10+8) name old sys-time/op new sys-time/op delta Referrers 16.1s ± 3% 15.9s ± 3% ~ (p=0.218 n=10+10) This work supported by Sourcegraph. Change-Id: I594ef25c0fd5ccb766ff5b98dbbd1a75a7a4f957 Reviewed-on: https://go-review.googlesource.com/108935 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
c1f4e2c6dc
commit
5c8013c561
|
@ -436,12 +436,15 @@ func globalReferrersPkgLevel(q *Query, obj types.Object, fset *token.FileSet) er
|
||||||
deffiles = make(map[string]*ast.File)
|
deffiles = make(map[string]*ast.File)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer) // reusable buffer for reading files
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if !buildutil.IsAbsPath(q.Build, file) {
|
if !buildutil.IsAbsPath(q.Build, file) {
|
||||||
file = buildutil.JoinPath(q.Build, pkg.Dir, file)
|
file = buildutil.JoinPath(q.Build, pkg.Dir, file)
|
||||||
}
|
}
|
||||||
|
buf.Reset()
|
||||||
sema <- struct{}{} // acquire token
|
sema <- struct{}{} // acquire token
|
||||||
src, err := readFile(q.Build, file)
|
src, err := readFile(q.Build, file, buf)
|
||||||
<-sema // release token
|
<-sema // release token
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
@ -731,7 +734,7 @@ func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string))
|
||||||
// start asynchronous read.
|
// start asynchronous read.
|
||||||
go func() {
|
go func() {
|
||||||
sema <- struct{}{} // acquire token
|
sema <- struct{}{} // acquire token
|
||||||
content, err := readFile(r.build, posn.Filename)
|
content, err := readFile(r.build, posn.Filename, nil)
|
||||||
<-sema // release token
|
<-sema // release token
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fi.data <- err
|
fi.data <- err
|
||||||
|
@ -769,14 +772,17 @@ func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string))
|
||||||
|
|
||||||
// readFile is like ioutil.ReadFile, but
|
// readFile is like ioutil.ReadFile, but
|
||||||
// it goes through the virtualized build.Context.
|
// it goes through the virtualized build.Context.
|
||||||
func readFile(ctxt *build.Context, filename string) ([]byte, error) {
|
// If non-nil, buf must have been reset.
|
||||||
|
func readFile(ctxt *build.Context, filename string, buf *bytes.Buffer) ([]byte, error) {
|
||||||
rc, err := buildutil.OpenFile(ctxt, filename)
|
rc, err := buildutil.OpenFile(ctxt, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rc.Close()
|
defer rc.Close()
|
||||||
var buf bytes.Buffer
|
if buf == nil {
|
||||||
if _, err := io.Copy(&buf, rc); err != nil {
|
buf = new(bytes.Buffer)
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(buf, rc); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
|
|
Loading…
Reference in New Issue