|
| 1 | +import Bench "mo:bench"; |
| 2 | + |
| 3 | +import Array "../src/Array"; |
| 4 | +import Nat "../src/Nat"; |
| 5 | +import PriorityQueue "../src/PriorityQueue"; |
| 6 | +import PriorityQueueSet "utils/PriorityQueueSet"; |
| 7 | +import Random "../src/Random"; |
| 8 | +import Runtime "../src/Runtime"; |
| 9 | +import Map "../src/Map"; |
| 10 | +import Text "../src/Text"; |
| 11 | + |
| 12 | +module { |
| 13 | + |
| 14 | + type PriorityQueueUpdateOperation<T> = { |
| 15 | + #Push : T; |
| 16 | + #Pop; |
| 17 | + #Clear |
| 18 | + }; |
| 19 | + |
| 20 | + // Generates a randomized sequence of PriorityQueueUpdateOperations on Nat values. |
| 21 | + // The distribution of operations is controlled by weights. |
| 22 | + // |
| 23 | + // randomSeed - seed for reproducible RNG |
| 24 | + // operationsCount – total number of operations to generate |
| 25 | + // maxValueExclusive – upper bound (exclusive) for values pushed into the queue |
| 26 | + // wPush – relative weight of #Push operations (values in [0, maxValueExclusive)) |
| 27 | + // wPop – relative weight of #Pop operations |
| 28 | + // wClear – relative weight of #Clear operations |
| 29 | + func genOpsNatRandom( |
| 30 | + randomSeed : Nat64, |
| 31 | + operationsCount : Nat, |
| 32 | + maxValueExclusive : Nat, |
| 33 | + wPush : Nat, |
| 34 | + wPop : Nat, |
| 35 | + wClear : Nat |
| 36 | + ) : [PriorityQueueUpdateOperation<Nat>] { |
| 37 | + let rng = Random.seed(randomSeed); |
| 38 | + Array.tabulate<PriorityQueueUpdateOperation<Nat>>( |
| 39 | + operationsCount, |
| 40 | + func(_) { |
| 41 | + let aux = rng.natRange(0, wPush + wPop + wClear); |
| 42 | + if (aux < wPush) { |
| 43 | + #Push(rng.natRange(0, maxValueExclusive)) |
| 44 | + } else if (aux < wPush + wPop) { |
| 45 | + #Pop |
| 46 | + } else { |
| 47 | + #Clear |
| 48 | + } |
| 49 | + } |
| 50 | + ) |
| 51 | + }; |
| 52 | + |
| 53 | + // Generates a sequence of PriorityQueueUpdateOperations on Nat values: |
| 54 | + // 1. pushOperationsCount pushes, followed by |
| 55 | + // 2. pushOperationsCount pops. |
| 56 | + // |
| 57 | + // randomSeed - seed for reproducible RNG |
| 58 | + // pushOperationsCount – total number of push operations to generate |
| 59 | + // maxValueExclusive – upper bound (exclusive) for values pushed into the queue |
| 60 | + func genOpsPushThenPop( |
| 61 | + randomSeed : Nat64, |
| 62 | + pushOperationsCount : Nat, |
| 63 | + maxValueExclusive : Nat |
| 64 | + ) : [PriorityQueueUpdateOperation<Nat>] { |
| 65 | + let rng = Random.seed(randomSeed); |
| 66 | + Array.tabulate<PriorityQueueUpdateOperation<Nat>>( |
| 67 | + 2 * pushOperationsCount, |
| 68 | + func(i) { |
| 69 | + switch (i < pushOperationsCount) { |
| 70 | + case true #Push(rng.natRange(0, maxValueExclusive)); |
| 71 | + case false #Pop |
| 72 | + } |
| 73 | + } |
| 74 | + ) |
| 75 | + }; |
| 76 | + |
| 77 | + // Generates a sequence of PriorityQueueUpdateOperations on Nat values: |
| 78 | + // 1. size pushes, followed by |
| 79 | + // 2. popPushCount instances of a pop followed by a push. |
| 80 | + // |
| 81 | + // randomSeed - seed for reproducible RNG |
| 82 | + // size – initial size |
| 83 | + // popPushCount - number of times to pop and then push |
| 84 | + // maxValueExclusive – upper bound (exclusive) for values pushed into the queue |
| 85 | + func genOpsKeepConstantSize( |
| 86 | + randomSeed : Nat64, |
| 87 | + size : Nat, |
| 88 | + popPushCount : Nat, |
| 89 | + maxValueExclusive : Nat |
| 90 | + ) : [PriorityQueueUpdateOperation<Nat>] { |
| 91 | + let rng = Random.seed(randomSeed); |
| 92 | + Array.tabulate<PriorityQueueUpdateOperation<Nat>>( |
| 93 | + size + 2 * popPushCount, |
| 94 | + func(i) { |
| 95 | + if (i < size or (i - size) % 2 == 1) { |
| 96 | + #Push(rng.natRange(0, maxValueExclusive)) |
| 97 | + } else { |
| 98 | + #Pop |
| 99 | + } |
| 100 | + } |
| 101 | + ) |
| 102 | + }; |
| 103 | + |
| 104 | + public func init() : Bench.Bench { |
| 105 | + let bench = Bench.Bench(); |
| 106 | + |
| 107 | + bench.name("Different priority queue implementations"); |
| 108 | + bench.description("Compare the performance of the following priority queue implementations: |
| 109 | +- `PriorityQueue`: Binary heap implementation over `List`. |
| 110 | +- `PriorityQueueSet`: Wrapper over `Set<(T, Nat)>`."); |
| 111 | + |
| 112 | + let testInstances : Map.Map<Text, [PriorityQueueUpdateOperation<Nat>]> = Map.fromIter( |
| 113 | + [ |
| 114 | + ( |
| 115 | + "1.) 100000 operations (push:pop = 1:1)", |
| 116 | + genOpsNatRandom( |
| 117 | + /* randomSeed = */ 23, |
| 118 | + /* operationsCount = */ 100000, |
| 119 | + /* maxValueExclusive = */ 100000, |
| 120 | + /* wPush = */ 1, |
| 121 | + /* wPop = */ 1, |
| 122 | + /* wClear = */ 0 |
| 123 | + ) |
| 124 | + ), |
| 125 | + ( |
| 126 | + "2.) 100000 operations (push:pop = 2:1)", |
| 127 | + genOpsNatRandom( |
| 128 | + /* randomSeed = */ 24, |
| 129 | + /* operationsCount = */ 100000, |
| 130 | + /* maxValueExclusive = */ 100000, |
| 131 | + /* wPush = */ 2, |
| 132 | + /* wPop = */ 1, |
| 133 | + /* wClear = */ 0 |
| 134 | + ) |
| 135 | + ), |
| 136 | + ( |
| 137 | + "3.) 100000 operations (push:pop = 10:1)", |
| 138 | + genOpsNatRandom( |
| 139 | + /* randomSeed = */ 42, |
| 140 | + /* operationsCount = */ 100000, |
| 141 | + /* maxValueExclusive = */ 100000, |
| 142 | + /* wPush = */ 10, |
| 143 | + /* wPop = */ 1, |
| 144 | + /* wClear = */ 0 |
| 145 | + ) |
| 146 | + ), |
| 147 | + ( |
| 148 | + "4.) 100000 operations (only push)", |
| 149 | + genOpsNatRandom( |
| 150 | + /* randomSeed = */ 33, |
| 151 | + /* operationsCount = */ 100000, |
| 152 | + /* maxValueExclusive = */ 100000, |
| 153 | + /* wPush = */ 1, |
| 154 | + /* wPop = */ 0, |
| 155 | + /* wClear = */ 0 |
| 156 | + ) |
| 157 | + ), |
| 158 | + ( |
| 159 | + "5.) 50000 pushes, then 50000 pops", |
| 160 | + genOpsPushThenPop( |
| 161 | + /* randomSeed = */ 13, |
| 162 | + /* pushOperationsCount = */ 50000, |
| 163 | + /* maxValueExclusive */ 100000 |
| 164 | + ) |
| 165 | + ), |
| 166 | + ( |
| 167 | + "6.) 50000 pushes, then 25000 \"pop;push\"es", |
| 168 | + genOpsKeepConstantSize( |
| 169 | + /* randomSeed = */ 55, |
| 170 | + /* size = */ 50000, |
| 171 | + /* popPushCount = */ 25000, |
| 172 | + /* maxValueExclusive = */ 100000 |
| 173 | + ) |
| 174 | + ) |
| 175 | + ].values(), |
| 176 | + Text.compare |
| 177 | + ); |
| 178 | + bench.rows(Map.keys<Text, [PriorityQueueUpdateOperation<Nat>]>(testInstances) |> Array.fromIter(_)); |
| 179 | + |
| 180 | + let testRunners : Map.Map<Text, [PriorityQueueUpdateOperation<Nat>] -> ()> = Map.fromIter( |
| 181 | + [ |
| 182 | + ( |
| 183 | + "A) PriorityQueue", |
| 184 | + func(ops : [PriorityQueueUpdateOperation<Nat>]) { |
| 185 | + let priorityQueue = PriorityQueue.empty<Nat>(); |
| 186 | + for (op in ops.values()) { |
| 187 | + switch (op) { |
| 188 | + case (#Push element) PriorityQueue.push(priorityQueue, Nat.compare, element); |
| 189 | + case (#Pop) ignore PriorityQueue.pop(priorityQueue, Nat.compare); |
| 190 | + case (#Clear) PriorityQueue.clear(priorityQueue) |
| 191 | + } |
| 192 | + } |
| 193 | + } |
| 194 | + ), |
| 195 | + ( |
| 196 | + "B) PriorityQueueSet", |
| 197 | + func(ops : [PriorityQueueUpdateOperation<Nat>]) { |
| 198 | + let priorityQueueSet = PriorityQueueSet.empty<Nat>(); |
| 199 | + for (op in ops.values()) { |
| 200 | + switch (op) { |
| 201 | + case (#Push element) PriorityQueueSet.push(priorityQueueSet, Nat.compare, element); |
| 202 | + case (#Pop) ignore PriorityQueueSet.pop(priorityQueueSet, Nat.compare); |
| 203 | + case (#Clear) PriorityQueueSet.clear(priorityQueueSet) |
| 204 | + } |
| 205 | + } |
| 206 | + } |
| 207 | + ) |
| 208 | + ].values(), |
| 209 | + Text.compare |
| 210 | + ); |
| 211 | + bench.cols(Map.keys<Text, [PriorityQueueUpdateOperation<Nat>] -> ()>(testRunners) |> Array.fromIter(_)); |
| 212 | + |
| 213 | + bench.runner( |
| 214 | + func(row, col) { |
| 215 | + switch ( |
| 216 | + Map.get(testInstances, Text.compare, row), |
| 217 | + Map.get(testRunners, Text.compare, col) |
| 218 | + ) { |
| 219 | + case (?ops, ?runner) runner(ops); |
| 220 | + case _ Runtime.trap("Missing test instance or runner") |
| 221 | + } |
| 222 | + } |
| 223 | + ); |
| 224 | + bench |
| 225 | + } |
| 226 | +} |
0 commit comments