From 5c8013c5617234a9515f1b1379aba7e03e846479 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Mon, 23 Apr 2018 12:43:41 -0700 Subject: [PATCH] cmd/guru: re-use buffer for reading files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- cmd/guru/referrers.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cmd/guru/referrers.go b/cmd/guru/referrers.go index cf9b022e..215f8037 100644 --- a/cmd/guru/referrers.go +++ b/cmd/guru/referrers.go @@ -436,12 +436,15 @@ func globalReferrersPkgLevel(q *Query, obj types.Object, fset *token.FileSet) er deffiles = make(map[string]*ast.File) } + buf := new(bytes.Buffer) // reusable buffer for reading files + for _, file := range files { if !buildutil.IsAbsPath(q.Build, file) { file = buildutil.JoinPath(q.Build, pkg.Dir, file) } + buf.Reset() sema <- struct{}{} // acquire token - src, err := readFile(q.Build, file) + src, err := readFile(q.Build, file, buf) <-sema // release token if err != nil { continue @@ -731,7 +734,7 @@ func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string)) // start asynchronous read. go func() { sema <- struct{}{} // acquire token - content, err := readFile(r.build, posn.Filename) + content, err := readFile(r.build, posn.Filename, nil) <-sema // release token if err != nil { fi.data <- err @@ -769,14 +772,17 @@ func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string)) // readFile is like ioutil.ReadFile, but // 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) if err != nil { return nil, err } defer rc.Close() - var buf bytes.Buffer - if _, err := io.Copy(&buf, rc); err != nil { + if buf == nil { + buf = new(bytes.Buffer) + } + if _, err := io.Copy(buf, rc); err != nil { return nil, err } return buf.Bytes(), nil