Skip to content

Commit a9a01b0

Browse files
committed
improve stdlib parsing benchmark by not loading all stdlib into memory
1 parent 64baebd commit a9a01b0

File tree

2 files changed

+27
-8
lines changed

2 files changed

+27
-8
lines changed

src/cmd/compile/internal/syntax/parser_test.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ func TestVerify(t *testing.T) {
3939
verifyPrint(t, *src_, ast)
4040
}
4141

42+
// To run only this benchmark and obtain results for benchstat:
43+
// go test -bench=ParseStdLib -benchtime=5s -run none -count=20
4244
func BenchmarkParseStdLib(b *testing.B) {
4345
if testing.Short() {
4446
b.Skip("skipping test in short mode")
@@ -55,7 +57,8 @@ func BenchmarkParseStdLib(b *testing.B) {
5557
type file struct {
5658
name string
5759
base *PosBase
58-
data []byte
60+
data []byte // data populated only for files being tested.
61+
size int64
5962
}
6063
var files []file
6164
goroot := testenv.GOROOT(b)
@@ -71,25 +74,37 @@ func BenchmarkParseStdLib(b *testing.B) {
7174
fmt.Printf("skipping %s\n", filename)
7275
return
7376
}
74-
data, err := os.ReadFile(filename)
77+
info, err := os.Stat(filename)
7578
if err != nil {
7679
b.Fatal(err)
7780
}
7881
files = append(files, file{
7982
name: filename,
80-
data: data,
83+
size: info.Size(),
8184
base: NewFileBase(filename),
8285
})
8386
})
8487
}
85-
slices.SortStableFunc(files, func(a, b file) int {
86-
return len(a.data) - len(b.data)
87-
})
88-
b.ResetTimer()
8988
const numberOfFiles = 10
9089
if len(files) < numberOfFiles*2 {
9190
b.Error("too few files matched to run")
9291
}
92+
loadFile := func(f *file) {
93+
var err error
94+
f.data, err = os.ReadFile(f.name)
95+
if err != nil {
96+
b.Fatal(err)
97+
}
98+
}
99+
slices.SortStableFunc(files, func(a, b file) int {
100+
return int(a.size - b.size)
101+
})
102+
// We load the files we'll be testing into memory to avoid noise introduced by operating system.
103+
for i := 0; i < numberOfFiles; i++ {
104+
loadFile(&files[i]) // Load smallest files.
105+
loadFile(&files[len(files)-i-1]) // Load largest files.
106+
}
107+
b.ResetTimer()
93108
b.Run(fmt.Sprintf("longest %d files", numberOfFiles), func(b *testing.B) {
94109
var buf bytes.Reader
95110
for i := 0; i < b.N; i++ {
@@ -99,7 +114,6 @@ func BenchmarkParseStdLib(b *testing.B) {
99114
}
100115
}
101116
})
102-
103117
b.Run(fmt.Sprintf("shortest %d files", numberOfFiles), func(b *testing.B) {
104118
var buf bytes.Reader
105119
for i := 0; i < b.N; i++ {

src/go/token/token.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,14 @@ func (op Token) Precedence() int {
282282
// hash is a perfect hash function for keywords.
283283
// It assumes that s has at least length 2.
284284
func hash(s string) uint {
285+
// If you get collisions on adding a keyword you'll need to
286+
// process more bytes of the identifier since this'll indicate
287+
// two keywords share the same first two bytes.
288+
// Best course of action is incrementing keyword map size or tuning the hash operations.
285289
return (uint(s[0])<<4 ^ uint(s[1]) + uint(len(s))) & uint(len(keywordMap)-1)
286290
}
287291

292+
// keywordMap is a perfect map taken from src/cmd/compile/internal/syntax/scanner.go
288293
var keywordMap [1 << 6]Token // size must be power of two
289294

290295
func init() {

0 commit comments

Comments
 (0)