From 16381b145e606729332793cade647d8eb76c042b Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 2 May 2011 09:17:46 -0700 Subject: [PATCH] go/token: faster FileSet.Position implementation - added a cache for last file looked up: avoids binary search if the file matches - don't look up extra line info if not present (it is almost never present) - inline one critical binary search call (inlining provides almost 30% improvement in this case) Together, these changes make the go/printer benchmark more than twice as fast (53% improvement). gofmt also sped up by about the same amount. Also: removed an unused internal field from FileSet. Measurements (always best of 5 runs): * original: printer.BenchmarkPrint 5 238354200 ns/op (100%) * using last file cache: printer.BenchmarkPrint 10 201796600 ns/op (85%) * avoiding lookup of extra line info: printer.BenchmarkPrint 10 157072700 ns/op (66%) * inlining a critical binary search call: printer.BenchmarkPrint 10 111523500 ns/op (47%) gofmt (always best of 3 runs): * before: time gofmt -l src misc real 0m33.316s user 0m31.298s sys 0m0.319s * after: time gofmt -l src misc real 0m15.889s user 0m14.596s sys 0m0.224s R=r, dfc, bradfitz, rsc1 CC=golang-dev https://golang.org/cl/4433086 --- src/pkg/go/token/position.go | 52 ++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go index 809e53f0aa2..8c35eeb52f7 100644 --- a/src/pkg/go/token/position.go +++ b/src/pkg/go/token/position.go @@ -94,10 +94,14 @@ func searchFiles(a []*File, x int) int { func (s *FileSet) file(p Pos) *File { + if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size { + return f + } if i := searchFiles(s.files, int(p)); i >= 0 { f := s.files[i] // f.base <= int(p) by definition of searchFiles if int(p) <= f.base+f.size { + s.last = f return f } } @@ -316,8 +320,26 @@ func (f *File) Position(p Pos) (pos Position) { } -func searchUints(a []int, x int) int { - return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1 +func searchInts(a []int, x int) int { + // This function body is a manually inlined version of: + // + // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1 + // + // With better compiler optimizations, this may not be needed in the + // future, but at the moment this change improves the go/printer + // benchmark performance by ~30%. This has a direct impact on the + // speed of gofmt and thus seems worthwhile (2011-04-29). + i, j := 0, len(a) + for i < j { + h := i + (j-i)/2 // avoid overflow when computing h + // i ≤ h < j + if a[h] <= x { + i = h + 1 + } else { + j = h + } + } + return i - 1 } @@ -329,14 +351,17 @@ func searchLineInfos(a []lineInfo, x int) int { // info returns the file name, line, and column number for a file offset. func (f *File) info(offset int) (filename string, line, column int) { filename = f.name - if i := searchUints(f.lines, offset); i >= 0 { + if i := searchInts(f.lines, offset); i >= 0 { line, column = i+1, offset-f.lines[i]+1 } - if i := searchLineInfos(f.infos, offset); i >= 0 { - alt := &f.infos[i] - filename = alt.filename - if i := searchUints(f.lines, alt.offset); i >= 0 { - line += alt.line - i - 1 + if len(f.infos) > 0 { + // almost no files have extra line infos + if i := searchLineInfos(f.infos, offset); i >= 0 { + alt := &f.infos[i] + filename = alt.filename + if i := searchInts(f.lines, alt.offset); i >= 0 { + line += alt.line - i - 1 + } } } return @@ -348,10 +373,10 @@ func (f *File) info(offset int) (filename string, line, column int) { // may invoke them concurrently. // type FileSet struct { - mutex sync.RWMutex // protects the file set - base int // base offset for the next file - files []*File // list of files in the order added to the set - index map[*File]int // file -> files index for quick lookup + mutex sync.RWMutex // protects the file set + base int // base offset for the next file + files []*File // list of files in the order added to the set + last *File // cache of last file looked up } @@ -359,7 +384,6 @@ type FileSet struct { func NewFileSet() *FileSet { s := new(FileSet) s.base = 1 // 0 == NoPos - s.index = make(map[*File]int) return s } @@ -405,8 +429,8 @@ func (s *FileSet) AddFile(filename string, base, size int) *File { } // add the file to the file set s.base = base - s.index[f] = len(s.files) s.files = append(s.files, f) + s.last = f return f }