|
19 | 19 | [b backend]
|
20 | 20 | {:structure (core/b-tree (core/->Config b b 0))
|
21 | 21 | :insert core/insert
|
| 22 | + :delete core/delete |
22 | 23 | :flush (fn [x] (core/flush-tree x backend))})
|
23 | 24 |
|
24 | 25 | (defn msg-b-tree
|
|
27 | 28 | (let [sqrt-b (long (Math/sqrt b))]
|
28 | 29 | {:structure (core/b-tree(core/->Config sqrt-b b (- b sqrt-b)))
|
29 | 30 | :insert msg/insert
|
| 31 | + :delete msg/delete |
30 | 32 | :flush (fn [x] (core/flush-tree x backend))}))
|
31 | 33 |
|
32 | 34 | (defn sorted-set-repr
|
33 | 35 | "Returns a sorted set"
|
34 | 36 | []
|
35 | 37 | {:structure (sorted-set)
|
36 | 38 | :insert conj
|
| 39 | + :delete disj |
37 | 40 | :flush (fn [set]
|
38 | 41 | {:tree set
|
39 | 42 | :stats (atom {})})})
|
|
58 | 61 | flush-freq is the number of keys per flush
|
59 | 62 | datastruct is the test data structure
|
60 | 63 | out is the stream to write the results to (as well as stdout)"
|
61 |
| - [n dataset flush-freq datastruct out] |
62 |
| - (let [{:keys [structure insert flush]} datastruct] |
63 |
| - (loop [[x & data] (take n (:data dataset)) |
| 64 | + [n dataset flush-freq datastruct out delete-xform] |
| 65 | + (let [{:keys [structure delete insert flush]} datastruct |
| 66 | + dataset (take n (:data dataset))] |
| 67 | + (loop [[x & data] dataset |
64 | 68 | t 0
|
65 | 69 | tree structure
|
66 | 70 | last-flush nil
|
67 | 71 | i 0
|
| 72 | + inserting? true |
68 | 73 | outputs []]
|
69 | 74 | (let [i' (inc i)
|
70 | 75 | {flushed-tree :tree
|
71 | 76 | stats :stats} (when (zero? (mod i' flush-freq))
|
72 | 77 | (flush tree))
|
73 | 78 | before (System/nanoTime)
|
74 |
| - tree' (insert (or flushed-tree tree) x) |
| 79 | + tree' (if inserting? |
| 80 | + (insert (or flushed-tree tree) x) |
| 81 | + (delete (or flushed-tree tree) x)) |
75 | 82 | after (System/nanoTime)
|
76 | 83 | log-inserts (zero? (mod i' (quot n 100)))
|
77 | 84 | updated-outputs (atom outputs)]
|
|
80 | 87 | (let [ks (sort (keys last-flush))
|
81 | 88 | avg-ns (float (/ t (quot n 100)))]
|
82 | 89 | (when (zero? i)
|
83 |
| - (println (str "elements,insert_took_avg_ns," |
| 90 | + (println (str "elements,op,insert_took_avg_ns," |
84 | 91 | (str/join "," ks))))
|
85 |
| - (println (str i' "," avg-ns |
| 92 | + (println (str i' "," (if inserting? "insert" "delete") "," avg-ns |
86 | 93 | "," (str/join "," (map #(get last-flush %) ks))))
|
87 | 94 | (swap! updated-outputs conj (-> (into {} last-flush)
|
88 |
| - (assoc :avg-ns avg-ns |
89 |
| - :n i)))))) |
90 |
| - (if (seq data) |
| 95 | + (assoc :ins-avg-ns avg-ns |
| 96 | + (if inserting? |
| 97 | + :insert |
| 98 | + :delete) true |
| 99 | + :n i')))))) |
| 100 | + (cond |
| 101 | + (seq data) |
91 | 102 | (recur data
|
92 | 103 | (if log-inserts
|
93 | 104 | 0
|
94 | 105 | (+ t (- after before)))
|
95 | 106 | tree'
|
96 | 107 | (if stats (merge-with + last-flush @stats) last-flush)
|
97 | 108 | i'
|
| 109 | + inserting? |
98 | 110 | @updated-outputs)
|
| 111 | + inserting? |
| 112 | + (recur (delete-xform dataset) |
| 113 | + 0 |
| 114 | + tree' |
| 115 | + nil |
| 116 | + i' |
| 117 | + false |
| 118 | + @updated-outputs) |
| 119 | + :else |
99 | 120 | @updated-outputs)))))
|
100 | 121 |
|
101 | 122 | (def options
|
102 | 123 | [["-n" "--num-operations NUM_OPS" "The number of elements that will be applied to the data structure"
|
103 | 124 | :default 100000
|
104 | 125 | :parse-fn #(Long. %)
|
105 | 126 | :validate [pos? "n must be positive"]]
|
106 |
| - [nil "--data-structure" "Which data structure to run the test on" |
| 127 | + [nil "--data-structure STRUCT" "Which data structure to run the test on" |
107 | 128 | :default "fractal"
|
108 | 129 | :validate [#(#{"fractal" "b-tree" "sorted-set"} %) "Data structure must be fractal, b-tree, or sorted set"]]
|
109 | 130 | [nil "--backend testing" "Runs the benchmark with the specified backend"
|
110 | 131 | :default "testing"
|
111 | 132 | :validate [#(#{"redis" "testing"} %) "Backend must be redis or testing"]]
|
| 133 | + ["-d" "--delete-pattern PATTERN" "Specifies how the operations will be reordered on delete" |
| 134 | + :default "forward" |
| 135 | + :validate [#(#{"forward" "reverse" "shuffle" "zero"} %) "Incorrect delete pattern"] |
| 136 | + ] |
112 | 137 | [nil "--sorted-set" "Runs the benchmarks on a sorted set"]
|
113 | 138 | ["-b" "--tree-width" "Determines the width of the trees. Fractal trees use sqrt(b) child pointers; the rest is for messages."
|
114 | 139 | :default 300
|
|
138 | 163 | "Options:"
|
139 | 164 | options-summary
|
140 | 165 | ""
|
| 166 | + "Delete patterns:" |
| 167 | + "forward: we delete the elements in the order they were inserted" |
| 168 | + "reverse: we delete the elements in the reverse order they were inserted" |
| 169 | + "shuffle: we delete the elements in a random order" |
| 170 | + "zero: we repeatedly attempt to delete 0, thus never actually deleting" |
| 171 | + "" |
141 | 172 | "Backends:"
|
142 | 173 | "testing: this backend serializes nothing, just using an extra indirection"
|
143 | 174 | "redis: this backend uses a local redis server"]))
|
144 | 175 |
|
145 | 176 | (defn make-template-for-one-tree-freq-combo
|
146 |
| - [list-of-benchmark-results] |
147 |
| - (clojure.pprint/pprint list-of-benchmark-results) |
| 177 | + [list-of-benchmark-results filter-by] |
| 178 | + ;(clojure.pprint/pprint list-of-benchmark-results) |
148 | 179 | (assert (= 2 (count list-of-benchmark-results)) "Should be random and ordered")
|
149 | 180 | (let [indexed (group-by :ds list-of-benchmark-results)]
|
150 |
| - (map #(vector (:n %1) (:avg-ns %1) (:writes %1) (:avg-ns %2) (:writes %2)) |
151 |
| - (:results (first (get indexed "in-order"))) |
152 |
| - (:results (first (get indexed "random")))))) |
| 181 | + (map #(vector (:n %1) (:ins-avg-ns %1) (:writes %1) (:ins-avg-ns %2) (:writes %2)) |
| 182 | + (filter filter-by (:results (first (get indexed "in-order")))) |
| 183 | + (filter filter-by (:results (first (get indexed "random"))))))) |
153 | 184 |
|
154 | 185 | (defn template-one-sheet
|
155 | 186 | [pair-of-results-for-one-ds-config]
|
156 |
| - (let [{:keys [tree ds freq n b results]} (first pair-of-results-for-one-ds-config) |
| 187 | + (let [{:keys [tree ds freq n b results delete-pattern]} |
| 188 | + (first pair-of-results-for-one-ds-config) |
157 | 189 | x {;:sheet-name (str (name tree) " " ds " flushed every " freq)
|
158 | 190 | 0 [["Data Structure" (name tree) "" "n" n]]
|
159 |
| - 1 [["Flush Frequency" freq "" "b" b]] |
160 |
| - [5 104] (make-template-for-one-tree-freq-combo pair-of-results-for-one-ds-config)}] |
| 191 | + 1 [["Flush Frequency" freq "" "b" b "" "delete pattern" delete-pattern]] |
| 192 | + [5 18] (make-template-for-one-tree-freq-combo pair-of-results-for-one-ds-config :insert) |
| 193 | + [22 35] (make-template-for-one-tree-freq-combo pair-of-results-for-one-ds-config :delete)}] |
161 | 194 | x))
|
162 | 195 |
|
163 | 196 | (defn -main
|
164 | 197 | [root & args]
|
165 |
| - (doseq [args (->> args |
166 |
| - (partition-by #(= % "--")) |
167 |
| - (map-indexed vector) |
168 |
| - (filter (comp even? first)) |
169 |
| - (map second))] |
| 198 | + (doseq [args (or (->> args |
| 199 | + (partition-by #(= % "--")) |
| 200 | + (map-indexed vector) |
| 201 | + (filter (comp even? first)) |
| 202 | + (map second) |
| 203 | + (seq)) |
| 204 | + [[]])] ; always do one iteration |
170 | 205 | (let [{:keys [options arguments errors summary]} (parse-opts args options)
|
171 | 206 | tree-to-test (atom {})
|
172 | 207 | results (atom [])]
|
|
178 | 213 | "testing" (core/->TestingBackend)
|
179 | 214 | "redis" (redis/->RedisBackend
|
180 | 215 | #_(java.util.concurrent.Executors/newFixedThreadPool 4)))
|
| 216 | + delete-xform (case (:delete-pattern options) |
| 217 | + "forward" identity |
| 218 | + "reverse" reverse |
| 219 | + "shuffle" shuffle |
| 220 | + "zero" #(repeat (count %) 0.0)) |
181 | 221 | [tree-name structure]
|
182 | 222 | (case (:data-structure options)
|
183 | 223 | "b-tree" ["b-tree" (core-b-tree (:tree-width options) backend)]
|
184 | 224 | "fractal" ["fractal" (msg-b-tree (:tree-width options) backend)]
|
185 | 225 | "sorted-set" ["sorted-set" (sorted-set-repr)])
|
186 | 226 | flush-freq (:flush-freq options)
|
187 | 227 | codename (str tree-name
|
188 |
| - "_flush" |
| 228 | + "__flush_" |
189 | 229 | flush-freq
|
190 |
| - "_b" |
| 230 | + "__b_" |
191 | 231 | (:tree-width options)
|
192 |
| - "_" |
| 232 | + "__" |
193 | 233 | (:backend options)
|
194 |
| - "_n" |
195 |
| - (:num-operations options))] |
| 234 | + "__n_" |
| 235 | + (:num-operations options) |
| 236 | + "__del_" |
| 237 | + (:delete-pattern options))] |
196 | 238 | (doseq [ds (generate-test-datasets)
|
197 | 239 | :let [codename (str codename
|
198 | 240 | "_"
|
199 | 241 | (:name ds))
|
200 | 242 | out (create-output-dir
|
201 | 243 | root
|
202 |
| - codename)]] |
203 |
| - (println "Doing" codename) |
| 244 | + codename) |
| 245 | + _ (println "Doing" codename) |
| 246 | + bench-res (benchmark (:num-operations options) ds flush-freq structure out delete-xform)]] |
204 | 247 | (swap! results conj
|
205 | 248 | {:tree tree-name
|
206 | 249 | :ds (:name ds)
|
207 | 250 | :freq flush-freq
|
208 | 251 | :n (:num-operations options)
|
209 | 252 | :b (:tree-width options)
|
210 |
| - :results (benchmark (:num-operations options) ds flush-freq structure out)})) |
211 |
| - (excel/render-to-file |
212 |
| - "template_benchmark.xlsx" |
213 |
| - (.getPath (File. root (str codename "_analysis.xlsx"))) |
214 |
| - {"SingleDS" |
215 |
| - (template-one-sheet @results)}))))) |
| 253 | + :delete-pattern (:delete-pattern options) |
| 254 | + :results bench-res})) |
| 255 | + ;(println "results") |
| 256 | + ;(clojure.pprint/pprint @results) |
| 257 | + (excel/render-to-file |
| 258 | + "template_benchmark.xlsx" |
| 259 | + (.getPath (File. root (str codename "_analysis.xlsx"))) |
| 260 | + {"SingleDS" |
| 261 | + (template-one-sheet @results)}))))) |
0 commit comments