github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/opbench/config.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package opbench 12 13 import ( 14 "bytes" 15 "fmt" 16 "sort" 17 ) 18 19 // Options denotes a single input to a plan, along with the set of legal 20 // values for that input. 21 type Options struct { 22 Field string 23 Values []float64 24 } 25 26 // Configuration is a particular set of inputs. A Configuration totally defines 27 // how the parameters for a given plan should be set. 28 type Configuration map[string]float64 29 30 func (c Configuration) String() string { 31 // Configurations are stringified as "a=1/b=2/c=3/...". 32 keys := make([]string, len(c)) 33 i := 0 34 for k := range c { 35 keys[i] = k 36 i++ 37 } 38 39 // Sort the keys so that the stringified form is consistent. 40 sort.Strings(keys) 41 42 var buf bytes.Buffer 43 for i, k := range keys { 44 if i > 0 { 45 buf.WriteByte('/') 46 } 47 buf.WriteString(k) 48 buf.WriteByte('=') 49 fmt.Fprintf(&buf, "%d", int(c[k])) 50 } 51 return buf.String() 52 } 53 54 // ConfigIterator takes a list of Options and produces every possible 55 // combination of them. 56 // TODO(justin): we should also support some kind of consistent sampling of 57 // these so we don't necessarily have to run them all. 58 type ConfigIterator struct { 59 options []Options 60 61 // state tracks the current index for each choice. It always has the same 62 // length as options. 63 state []int 64 done bool 65 } 66 67 // NewConfigIterator returns a ConfigIterator that iterates over all of the 68 // possible inputs to the given Spec. 69 func NewConfigIterator(spec *Spec) *ConfigIterator { 70 return &ConfigIterator{ 71 options: spec.Inputs, 72 state: make([]int, len(spec.Inputs)), 73 } 74 } 75 76 // Next returns the next Configuration in the iteration process. 77 func (it *ConfigIterator) Next() (Configuration, bool) { 78 if it.done { 79 return nil, false 80 } 81 config := make(Configuration, len(it.options)) 82 for j := range it.options { 83 config[it.options[j].Field] = it.options[j].Values[it.state[j]] 84 } 85 it.increment() 86 87 return config, true 88 } 89 90 // increment brings the iterator to the next state, given the maximum possible 91 // value for each "slot". So if the first option has 2 choices and the second 92 // has 3, the increment process goes like: 93 // 94 // [0 0] => [1 0] => [0 1] => [1 1] => [0 2] => [1 2] => done. 95 func (it *ConfigIterator) increment() { 96 i := 0 97 for i < len(it.options) { 98 it.state[i]++ 99 if it.state[i] < len(it.options[i].Values) { 100 break 101 } 102 it.state[i] = 0 103 i++ 104 } 105 if i == len(it.options) { 106 it.done = true 107 } 108 }