github.com/rajeev159/opa@v0.45.0/ast/term_bench_test.go (about) 1 // Copyright 2017 The OPA Authors. All rights reserved. 2 // Use of this source code is governed by an Apache2 3 // license that can be found in the LICENSE file. 4 package ast 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "math/rand" 10 "strings" 11 "testing" 12 "time" 13 ) 14 15 func BenchmarkObjectLookup(b *testing.B) { 16 sizes := []int{5, 50, 500, 5000} 17 for _, n := range sizes { 18 b.Run(fmt.Sprint(n), func(b *testing.B) { 19 obj := NewObject() 20 for i := 0; i < n; i++ { 21 obj.Insert(StringTerm(fmt.Sprint(i)), IntNumberTerm(i)) 22 } 23 key := StringTerm(fmt.Sprint(n - 1)) 24 b.ResetTimer() 25 for i := 0; i < b.N; i++ { 26 value := obj.Get(key) 27 if value == nil { 28 b.Fatal("expected hit") 29 } 30 } 31 }) 32 } 33 } 34 35 func BenchmarkSetIntersection(b *testing.B) { 36 sizes := []int{5, 50, 500, 5000} 37 for _, n := range sizes { 38 b.Run(fmt.Sprint(n), func(b *testing.B) { 39 setA := NewSet() 40 setB := NewSet() 41 for i := 0; i < n; i++ { 42 setA.Add(IntNumberTerm(i)) 43 setB.Add(IntNumberTerm(i)) 44 } 45 b.ResetTimer() 46 for i := 0; i < b.N; i++ { 47 setC := setA.Intersect(setB) 48 if setC.Len() != setA.Len() || setC.Len() != setB.Len() { 49 b.Fatal("expected equal") 50 } 51 } 52 }) 53 } 54 } 55 56 func BenchmarkSetIntersectionDifferentSize(b *testing.B) { 57 sizes := []int{4, 50, 500, 5000} 58 for _, n := range sizes { 59 b.Run(fmt.Sprint(n), func(b *testing.B) { 60 setA := NewSet() 61 setB := NewSet() 62 for i := 0; i < n; i++ { 63 setA.Add(IntNumberTerm(i)) 64 } 65 for i := 0; i < sizes[0]; i++ { 66 setB.Add(IntNumberTerm(i)) 67 } 68 setB.Add(IntNumberTerm(-1)) 69 b.ResetTimer() 70 for i := 0; i < b.N; i++ { 71 setC := setA.Intersect(setB) 72 if setC.Len() != sizes[0] { 73 b.Fatal("expected size to be equal") 74 } 75 } 76 }) 77 } 78 } 79 80 func BenchmarkSetMembership(b *testing.B) { 81 sizes := []int{5, 50, 500, 5000} 82 for _, n := range sizes { 83 b.Run(fmt.Sprint(n), func(b *testing.B) { 84 setA := NewSet() 85 for i := 0; i < n; i++ { 86 setA.Add(IntNumberTerm(i)) 87 } 88 key := IntNumberTerm(n - 1) 89 b.ResetTimer() 90 for i := 0; i < b.N; i++ { 91 if !setA.Contains(key) { 92 b.Fatal("expected hit") 93 } 94 } 95 }) 96 } 97 } 98 99 func BenchmarkTermHashing(b *testing.B) { 100 sizes := []int{10, 100, 1000} 101 for _, n := range sizes { 102 b.Run(fmt.Sprint(n), func(b *testing.B) { 103 s := String(strings.Repeat("a", n)) 104 b.ResetTimer() 105 for i := 0; i < b.N; i++ { 106 _ = s.Hash() 107 } 108 }) 109 } 110 } 111 112 var str string 113 var bs []byte 114 115 // BenchmarkObjectString generates several objects of different sizes, and 116 // marshals them to JSON via two ways: 117 // map[string]int -> ast.Value -> .String() 118 // and 119 // map[string]int -> json.Marshal() 120 // 121 // The difference between these two is relevant for feeding input into the 122 // wasm vm: when calling rego.New(...) with rego.Target("wasm"), it's up to 123 // the caller to provide the input in parsed form (ast.Value), or 124 // raw (interface{}). 125 func BenchmarkObjectString(b *testing.B) { 126 var err error 127 sizes := []int{5, 50, 500, 5000} 128 129 for _, n := range sizes { 130 b.Run(fmt.Sprint(n), func(b *testing.B) { 131 132 obj := map[string]int{} 133 for i := 0; i < n; i++ { 134 obj[fmt.Sprint(i)] = i 135 } 136 val := MustInterfaceToValue(obj) 137 138 b.Run("String()", func(b *testing.B) { 139 b.ResetTimer() 140 for i := 0; i < b.N; i++ { 141 str = val.String() 142 } 143 }) 144 b.Run("json.Marshal", func(b *testing.B) { 145 b.ResetTimer() 146 for i := 0; i < b.N; i++ { 147 bs, err = json.Marshal(obj) 148 if err != nil { 149 b.Fatal(err) 150 } 151 } 152 }) 153 }) 154 } 155 } 156 157 func BenchmarkObjectConstruction(b *testing.B) { 158 sizes := []int{5, 50, 500, 5000, 50000} 159 seed := time.Now().UnixNano() 160 161 b.Run("shuffled keys", func(b *testing.B) { 162 for _, n := range sizes { 163 b.Run(fmt.Sprint(n), func(b *testing.B) { 164 es := []struct{ k, v int }{} 165 for i := 0; i < n; i++ { 166 es = append(es, struct{ k, v int }{i, i}) 167 } 168 rand.Seed(seed) 169 rand.Shuffle(len(es), func(i, j int) { es[i], es[j] = es[j], es[i] }) 170 b.ResetTimer() 171 for i := 0; i < b.N; i++ { 172 obj := NewObject() 173 for _, e := range es { 174 obj.Insert(IntNumberTerm(e.k), IntNumberTerm(e.v)) 175 } 176 } 177 }) 178 } 179 }) 180 b.Run("increasing keys", func(b *testing.B) { 181 for _, n := range sizes { 182 b.Run(fmt.Sprint(n), func(b *testing.B) { 183 es := []struct{ k, v int }{} 184 for v := 0; v < n; v++ { 185 es = append(es, struct{ k, v int }{v, v}) 186 } 187 b.ResetTimer() 188 for i := 0; i < b.N; i++ { 189 obj := NewObject() 190 for _, e := range es { 191 obj.Insert(IntNumberTerm(e.k), IntNumberTerm(e.v)) 192 } 193 } 194 }) 195 } 196 }) 197 } 198 199 // BenchmarkArrayString compares the performance characteristics of 200 // (ast.Value).String() with the stdlib-native json.Marshal. See 201 // BenchmarkObjectString above for details. 202 func BenchmarkArrayString(b *testing.B) { 203 var err error 204 sizes := []int{5, 50, 500, 5000} 205 206 for _, n := range sizes { 207 b.Run(fmt.Sprint(n), func(b *testing.B) { 208 209 obj := make([]string, n) 210 for i := 0; i < n; i++ { 211 obj[i] = fmt.Sprint(i) 212 } 213 val := MustInterfaceToValue(obj) 214 215 b.Run("String()", func(b *testing.B) { 216 b.ResetTimer() 217 for i := 0; i < b.N; i++ { 218 str = val.String() 219 } 220 }) 221 b.Run("json.Marshal", func(b *testing.B) { 222 b.ResetTimer() 223 for i := 0; i < b.N; i++ { 224 bs, err = json.Marshal(obj) 225 if err != nil { 226 b.Fatal(err) 227 } 228 } 229 }) 230 }) 231 } 232 } 233 234 func BenchmarkSetString(b *testing.B) { 235 sizes := []int{5, 50, 500, 5000} 236 237 for _, n := range sizes { 238 b.Run(fmt.Sprint(n), func(b *testing.B) { 239 240 val := NewSet() 241 for i := 0; i < n; i++ { 242 val.Add(IntNumberTerm(i)) 243 } 244 245 b.Run("String()", func(b *testing.B) { 246 b.ResetTimer() 247 for i := 0; i < b.N; i++ { 248 str = val.String() 249 } 250 }) 251 }) 252 } 253 }