mirror of
https://github.com/golang/go
synced 2024-11-22 04:04:40 -07:00
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
This commit is contained in:
parent
6af887ec03
commit
16381b145e
@ -94,10 +94,14 @@ func searchFiles(a []*File, x int) int {
|
|||||||
|
|
||||||
|
|
||||||
func (s *FileSet) file(p Pos) *File {
|
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 {
|
if i := searchFiles(s.files, int(p)); i >= 0 {
|
||||||
f := s.files[i]
|
f := s.files[i]
|
||||||
// f.base <= int(p) by definition of searchFiles
|
// f.base <= int(p) by definition of searchFiles
|
||||||
if int(p) <= f.base+f.size {
|
if int(p) <= f.base+f.size {
|
||||||
|
s.last = f
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,8 +320,26 @@ func (f *File) Position(p Pos) (pos Position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func searchUints(a []int, x int) int {
|
func searchInts(a []int, x int) int {
|
||||||
return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1
|
// 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,16 +351,19 @@ func searchLineInfos(a []lineInfo, x int) int {
|
|||||||
// info returns the file name, line, and column number for a file offset.
|
// info returns the file name, line, and column number for a file offset.
|
||||||
func (f *File) info(offset int) (filename string, line, column int) {
|
func (f *File) info(offset int) (filename string, line, column int) {
|
||||||
filename = f.name
|
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
|
line, column = i+1, offset-f.lines[i]+1
|
||||||
}
|
}
|
||||||
|
if len(f.infos) > 0 {
|
||||||
|
// almost no files have extra line infos
|
||||||
if i := searchLineInfos(f.infos, offset); i >= 0 {
|
if i := searchLineInfos(f.infos, offset); i >= 0 {
|
||||||
alt := &f.infos[i]
|
alt := &f.infos[i]
|
||||||
filename = alt.filename
|
filename = alt.filename
|
||||||
if i := searchUints(f.lines, alt.offset); i >= 0 {
|
if i := searchInts(f.lines, alt.offset); i >= 0 {
|
||||||
line += alt.line - i - 1
|
line += alt.line - i - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,7 +376,7 @@ type FileSet struct {
|
|||||||
mutex sync.RWMutex // protects the file set
|
mutex sync.RWMutex // protects the file set
|
||||||
base int // base offset for the next file
|
base int // base offset for the next file
|
||||||
files []*File // list of files in the order added to the set
|
files []*File // list of files in the order added to the set
|
||||||
index map[*File]int // file -> files index for quick lookup
|
last *File // cache of last file looked up
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -359,7 +384,6 @@ type FileSet struct {
|
|||||||
func NewFileSet() *FileSet {
|
func NewFileSet() *FileSet {
|
||||||
s := new(FileSet)
|
s := new(FileSet)
|
||||||
s.base = 1 // 0 == NoPos
|
s.base = 1 // 0 == NoPos
|
||||||
s.index = make(map[*File]int)
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,8 +429,8 @@ func (s *FileSet) AddFile(filename string, base, size int) *File {
|
|||||||
}
|
}
|
||||||
// add the file to the file set
|
// add the file to the file set
|
||||||
s.base = base
|
s.base = base
|
||||||
s.index[f] = len(s.files)
|
|
||||||
s.files = append(s.files, f)
|
s.files = append(s.files, f)
|
||||||
|
s.last = f
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user