]> Cypherpunks repositories - gostls13.git/commitdiff
go/token: faster FileSet.Position implementation
authorRobert Griesemer <gri@golang.org>
Mon, 2 May 2011 16:17:46 +0000 (09:17 -0700)
committerRobert Griesemer <gri@golang.org>
Mon, 2 May 2011 16:17:46 +0000 (09:17 -0700)
- 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

index 809e53f0aa23d673be3929284ffbf68c07f82edc..8c35eeb52f77afa63f43c89009f27708962d6cb8 100644 (file)
@@ -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
 }