blob: 3649f144495cbb4707114082ad35ea291f7ffc4a [file] [log] [blame]
Alan Donovan312d1a52017-10-02 10:10:28 -04001// Copyright 2017 The Bazel Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Alan Donovane3deafe2018-10-23 11:05:09 -04005package starlark
Alan Donovan312d1a52017-10-02 10:10:28 -04006
7import (
alandonovanf94b0212018-10-12 15:31:48 -04008 "fmt"
Alan Donovan312d1a52017-10-02 10:10:28 -04009 "math/rand"
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080010 "sync"
Alan Donovan312d1a52017-10-02 10:10:28 -040011 "testing"
12)
13
14func TestHashtable(t *testing.T) {
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080015 makeTestIntsOnce.Do(makeTestInts)
Alan Donovan312d1a52017-10-02 10:10:28 -040016 testHashtable(t, make(map[int]bool))
17}
18
alandonovanf94b0212018-10-12 15:31:48 -040019func BenchmarkStringHash(b *testing.B) {
20 for len := 1; len <= 1024; len *= 2 {
21 buf := make([]byte, len)
22 rand.New(rand.NewSource(0)).Read(buf)
23 s := string(buf)
24
25 b.Run(fmt.Sprintf("hard-%d", len), func(b *testing.B) {
26 for i := 0; i < b.N; i++ {
27 hashString(s)
28 }
29 })
30 b.Run(fmt.Sprintf("soft-%d", len), func(b *testing.B) {
31 for i := 0; i < b.N; i++ {
32 softHashString(s)
33 }
34 })
35 }
36}
37
Alan Donovan312d1a52017-10-02 10:10:28 -040038func BenchmarkHashtable(b *testing.B) {
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080039 makeTestIntsOnce.Do(makeTestInts)
40 b.ResetTimer()
Alan Donovan312d1a52017-10-02 10:10:28 -040041 for i := 0; i < b.N; i++ {
42 testHashtable(b, nil)
43 }
44}
45
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080046const testIters = 10000
47
48var (
49 // testInts is a zipf-distributed array of Ints and corresponding ints.
50 // This removes the cost of generating them on the fly during benchmarking.
51 // Without this, Zipf and MakeInt dominate CPU and memory costs, respectively.
52 makeTestIntsOnce sync.Once
53 testInts [3 * testIters]struct {
54 Int Int
55 goInt int
Josh Bleecher Snyder107fc202018-11-24 08:47:54 -080056 }
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080057)
58
59func makeTestInts() {
60 zipf := rand.NewZipf(rand.New(rand.NewSource(0)), 1.1, 1.0, 1000.0)
61 for i := range &testInts {
62 r := int(zipf.Uint64())
63 testInts[i].goInt = r
64 testInts[i].Int = MakeInt(r)
65 }
Josh Bleecher Snyder107fc202018-11-24 08:47:54 -080066}
67
Alan Donovan312d1a52017-10-02 10:10:28 -040068// testHashtable is both a test and a benchmark of hashtable.
69// When sane != nil, it acts as a test against the semantics of Go's map.
70func testHashtable(tb testing.TB, sane map[int]bool) {
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080071 var i int // index into testInts
Josh Bleecher Snyder107fc202018-11-24 08:47:54 -080072
Alan Donovan312d1a52017-10-02 10:10:28 -040073 var ht hashtable
74
75 // Insert 10000 random ints into the map.
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080076 for j := 0; j < testIters; j++ {
77 k := testInts[i]
Josh Bleecher Snyder107fc202018-11-24 08:47:54 -080078 i++
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080079 if err := ht.insert(k.Int, None); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -040080 tb.Fatal(err)
81 }
82 if sane != nil {
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080083 sane[k.goInt] = true
Alan Donovan312d1a52017-10-02 10:10:28 -040084 }
85 }
86
87 // Do 10000 random lookups in the map.
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080088 for j := 0; j < testIters; j++ {
89 k := testInts[i]
Josh Bleecher Snyder107fc202018-11-24 08:47:54 -080090 i++
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080091 _, found, err := ht.lookup(k.Int)
Alan Donovan312d1a52017-10-02 10:10:28 -040092 if err != nil {
93 tb.Fatal(err)
94 }
95 if sane != nil {
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -080096 _, found2 := sane[k.goInt]
Alan Donovan312d1a52017-10-02 10:10:28 -040097 if found != found2 {
98 tb.Fatal("sanity check failed")
99 }
100 }
101 }
102
103 // Do 10000 random deletes from the map.
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -0800104 for j := 0; j < testIters; j++ {
105 k := testInts[i]
Josh Bleecher Snyder107fc202018-11-24 08:47:54 -0800106 i++
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -0800107 _, found, err := ht.delete(k.Int)
Alan Donovan312d1a52017-10-02 10:10:28 -0400108 if err != nil {
109 tb.Fatal(err)
110 }
111 if sane != nil {
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -0800112 _, found2 := sane[k.goInt]
Alan Donovan312d1a52017-10-02 10:10:28 -0400113 if found != found2 {
114 tb.Fatal("sanity check failed")
115 }
Josh Bleecher Snyder5eb2bff2018-11-30 14:04:33 -0800116 delete(sane, k.goInt)
Alan Donovan312d1a52017-10-02 10:10:28 -0400117 }
118 }
119
120 if sane != nil {
121 if int(ht.len) != len(sane) {
122 tb.Fatal("sanity check failed")
123 }
124 }
125}