github.com/etecs-ru/ristretto@v0.9.1/sim/sim.go (about) 1 /* 2 * Copyright 2019 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package sim 18 19 import ( 20 "bufio" 21 "errors" 22 "fmt" 23 "io" 24 "math/rand" 25 "strconv" 26 "strings" 27 "time" 28 ) 29 30 var ( 31 // ErrDone is returned when the underlying file has ran out of lines. 32 ErrDone = errors.New("no more values in the Simulator") 33 // ErrBadLine is returned when the trace file line is unrecognizable to 34 // the Parser. 35 ErrBadLine = errors.New("bad line for trace format") 36 ) 37 38 // Simulator is the central type of the `sim` package. It is a function 39 // returning a key from some source (composed from the other functions in this 40 // package, either generated or parsed). You can use these Simulators to 41 // approximate access distributions. 42 type Simulator func() (uint64, error) 43 44 // NewZipfian creates a Simulator returning numbers following a Zipfian [1] 45 // distribution infinitely. Zipfian distributions are useful for simulating real 46 // workloads. 47 // 48 // [1]: https://en.wikipedia.org/wiki/Zipf%27s_law 49 func NewZipfian(s, v float64, n uint64) Simulator { 50 z := rand.NewZipf(rand.New(rand.NewSource(time.Now().UnixNano())), s, v, n) 51 return func() (uint64, error) { 52 return z.Uint64(), nil 53 } 54 } 55 56 // NewUniform creates a Simulator returning uniformly distributed [1] (random) 57 // numbers [0, max) infinitely. 58 // 59 // [1]: https://en.wikipedia.org/wiki/Uniform_distribution_(continuous) 60 func NewUniform(max uint64) Simulator { 61 m := int64(max) 62 r := rand.New(rand.NewSource(time.Now().UnixNano())) 63 return func() (uint64, error) { 64 return uint64(r.Int63n(m)), nil 65 } 66 } 67 68 // Parser is used as a parameter to NewReader so we can create Simulators from 69 // varying trace file formats easily. 70 type Parser func(string, error) ([]uint64, error) 71 72 // NewReader creates a Simulator from two components: the Parser, which is a 73 // filetype specific function for parsing lines, and the file itself, which will 74 // be read from. 75 // 76 // When every line in the file has been read, ErrDone will be returned. For some 77 // trace formats (LIRS) there is one item per line. For others (ARC) there is a 78 // range of items on each line. Thus, the true number of items in each file 79 // is hard to determine, so it's up to the user to handle ErrDone accordingly. 80 func NewReader(parser Parser, file io.Reader) Simulator { 81 b := bufio.NewReader(file) 82 s := make([]uint64, 0) 83 i := -1 84 var err error 85 return func() (uint64, error) { 86 // only parse a new line when we've run out of items 87 if i++; i == len(s) { 88 // parse sequence from line 89 if s, err = parser(b.ReadString('\n')); err != nil { 90 s = []uint64{0} 91 } 92 i = 0 93 } 94 return s[i], err 95 } 96 } 97 98 // ParseLIRS takes a single line of input from a LIRS trace file as described in 99 // multiple papers [1] and returns a slice containing one number. A nice 100 // collection of LIRS trace files can be found in Ben Manes' repo [2]. 101 // 102 // [1]: https://en.wikipedia.org/wiki/LIRS_caching_algorithm 103 // [2]: https://git.io/fj9gU 104 func ParseLIRS(line string, err error) ([]uint64, error) { 105 if line = strings.TrimSpace(line); line != "" { 106 // example: "1\r\n" 107 key, err := strconv.ParseUint(line, 10, 64) 108 return []uint64{key}, err 109 } 110 return nil, ErrDone 111 } 112 113 // ParseARC takes a single line of input from an ARC trace file as described in 114 // "ARC: a self-tuning, low overhead replacement cache" [1] by Nimrod Megiddo 115 // and Dharmendra S. Modha [1] and returns a sequence of numbers generated from 116 // the line and any error. For use with NewReader. 117 // 118 // [1]: https://scinapse.io/papers/1860107648 119 func ParseARC(line string, err error) ([]uint64, error) { 120 if line != "" { 121 // example: "0 5 0 0\n" 122 // 123 // - first block: starting number in sequence 124 // - second block: number of items in sequence 125 // - third block: ignore 126 // - fourth block: global line number (not used) 127 cols := strings.Fields(line) 128 if len(cols) != 4 { 129 return nil, ErrBadLine 130 } 131 start, err := strconv.ParseUint(cols[0], 10, 64) 132 if err != nil { 133 return nil, err 134 } 135 count, err := strconv.ParseUint(cols[1], 10, 64) 136 if err != nil { 137 return nil, err 138 } 139 // populate sequence from start to start + count 140 seq := make([]uint64, count) 141 for i := range seq { 142 seq[i] = start + uint64(i) 143 } 144 return seq, nil 145 } 146 return nil, ErrDone 147 } 148 149 // Collection evaluates the Simulator size times and saves each item to the 150 // returned slice. 151 func Collection(simulator Simulator, size uint64) []uint64 { 152 collection := make([]uint64, size) 153 for i := range collection { 154 collection[i], _ = simulator() 155 } 156 return collection 157 } 158 159 // StringCollection evaluates the Simulator size times and saves each item to 160 // the returned slice, after converting it to a string. 161 func StringCollection(simulator Simulator, size uint64) []string { 162 collection := make([]string, size) 163 for i := range collection { 164 n, _ := simulator() 165 collection[i] = fmt.Sprintf("%d", n) 166 } 167 return collection 168 }