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  }