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  }