mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
go/token: benchmark FileSet.{Position,AddExistingFiles}
This CL adds a benchmark of FileSet.Position, the lookup operation, and the new AddExistingFiles. It is evident that its behavior is quadratic in important cases: (Apple M1) BenchmarkFileSet_AddExistingFiles/sequence-8 3 362768139 ns/op Change-Id: I256fdc776135e1924666d127afb37dacbefc860f Reviewed-on: https://go-review.googlesource.com/c/go/+/675875 Reviewed-by: Robert Findley <rfindley@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
ae0824883e
commit
c146a61d4c
2 changed files with 109 additions and 3 deletions
9
src/go/token/export_test.go
Normal file
9
src/go/token/export_test.go
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package token
|
||||||
|
|
||||||
|
// exports for tests
|
||||||
|
|
||||||
|
func SearchInts(a []int, x int) int { return searchInts(a, x) }
|
||||||
|
|
@ -2,9 +2,14 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package token
|
package token_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/build"
|
||||||
|
"go/token"
|
||||||
|
"math/rand/v2"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -14,11 +19,103 @@ func BenchmarkSearchInts(b *testing.B) {
|
||||||
data[i] = i
|
data[i] = i
|
||||||
}
|
}
|
||||||
const x = 8
|
const x = 8
|
||||||
if r := searchInts(data, x); r != x {
|
if r := token.SearchInts(data, x); r != x {
|
||||||
b.Errorf("got index = %d; want %d", r, x)
|
b.Errorf("got index = %d; want %d", r, x)
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
searchInts(data, x)
|
token.SearchInts(data, x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkFileSet_Position(b *testing.B) {
|
||||||
|
rng := rand.New(rand.NewPCG(rand.Uint64(), rand.Uint64()))
|
||||||
|
|
||||||
|
// Create a FileSet based on the files of net/http,
|
||||||
|
// a single large package.
|
||||||
|
netHTTPFset := token.NewFileSet()
|
||||||
|
pkg, err := build.Default.Import("net/http", "", 0)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, filename := range pkg.GoFiles {
|
||||||
|
filename = filepath.Join(pkg.Dir, filename)
|
||||||
|
fi, err := os.Stat(filename)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
netHTTPFset.AddFile(filename, -1, int(fi.Size()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure randomly distributed Pos values across net/http.
|
||||||
|
b.Run("random", func(b *testing.B) {
|
||||||
|
base := netHTTPFset.Base()
|
||||||
|
for b.Loop() {
|
||||||
|
pos := token.Pos(rng.IntN(base))
|
||||||
|
_ = netHTTPFset.Position(pos)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Measure random lookups within the same file of net/http.
|
||||||
|
// (It's faster because of the "last file" cache.)
|
||||||
|
b.Run("file", func(b *testing.B) {
|
||||||
|
var file *token.File
|
||||||
|
for file = range netHTTPFset.Iterate {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
base, size := file.Base(), file.Size()
|
||||||
|
for b.Loop() {
|
||||||
|
_ = netHTTPFset.Position(token.Pos(base + rng.IntN(size)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Measure random lookups on a FileSet with a great many files.
|
||||||
|
b.Run("manyfiles", func(b *testing.B) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
for range 25000 {
|
||||||
|
fset.AddFile("", -1, 10000)
|
||||||
|
}
|
||||||
|
base := fset.Base()
|
||||||
|
for b.Loop() {
|
||||||
|
pos := token.Pos(rng.IntN(base))
|
||||||
|
_ = fset.Position(pos)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFileSet_AddExistingFiles(b *testing.B) {
|
||||||
|
// Create the "universe" of files.
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
var files []*token.File
|
||||||
|
for range 25000 {
|
||||||
|
files = append(files, fset.AddFile("", -1, 10000))
|
||||||
|
}
|
||||||
|
rand.Shuffle(len(files), func(i, j int) {
|
||||||
|
files[i], files[j] = files[j], files[i]
|
||||||
|
})
|
||||||
|
|
||||||
|
// choose returns n random files.
|
||||||
|
choose := func(n int) []*token.File {
|
||||||
|
res := make([]*token.File, n)
|
||||||
|
for i := range res {
|
||||||
|
res[i] = files[rand.IntN(n)]
|
||||||
|
}
|
||||||
|
return files[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure the cost of creating a FileSet with a large number
|
||||||
|
// of files added in small handfuls, with some overlap.
|
||||||
|
// This case is critical to gopls.
|
||||||
|
b.Run("sequence", func(b *testing.B) {
|
||||||
|
for b.Loop() {
|
||||||
|
b.StopTimer()
|
||||||
|
fset2 := token.NewFileSet()
|
||||||
|
fset2.AddExistingFiles(files[:10000]...)
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for range 1000 {
|
||||||
|
fset2.AddExistingFiles(choose(10)...) // about one package
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue