diff options
author | Davidlohr Bueso <dave@stgolabs.net> | 2017-09-08 19:14:52 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-08 21:26:48 -0400 |
commit | b10d43f9898e0b56f6cf2375093dbd2c5db54486 (patch) | |
tree | 9a0b1da6a0972dd877ea5c36f05e9267a5554420 | |
parent | 977bd8d5e1e61dc877c468e8937a4ab3094e53eb (diff) |
lib/rbtree_test.c: support rb_root_cached
We can work with a single rb_root_cached root to test both cached and
non-cached rbtrees. In addition, also add a test to measure latencies
between rb_first and its fast counterpart.
Link: http://lkml.kernel.org/r/20170719014603.19029-7-dave@stgolabs.net
Signed-off-by: Davidlohr Bueso <dbueso@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | lib/rbtree_test.c | 156 |
1 files changed, 137 insertions, 19 deletions
diff --git a/lib/rbtree_test.c b/lib/rbtree_test.c index 967999ff46db..191a238e5a9d 100644 --- a/lib/rbtree_test.c +++ b/lib/rbtree_test.c | |||
@@ -23,14 +23,14 @@ struct test_node { | |||
23 | u32 augmented; | 23 | u32 augmented; |
24 | }; | 24 | }; |
25 | 25 | ||
26 | static struct rb_root root = RB_ROOT; | 26 | static struct rb_root_cached root = RB_ROOT_CACHED; |
27 | static struct test_node *nodes = NULL; | 27 | static struct test_node *nodes = NULL; |
28 | 28 | ||
29 | static struct rnd_state rnd; | 29 | static struct rnd_state rnd; |
30 | 30 | ||
31 | static void insert(struct test_node *node, struct rb_root *root) | 31 | static void insert(struct test_node *node, struct rb_root_cached *root) |
32 | { | 32 | { |
33 | struct rb_node **new = &root->rb_node, *parent = NULL; | 33 | struct rb_node **new = &root->rb_root.rb_node, *parent = NULL; |
34 | u32 key = node->key; | 34 | u32 key = node->key; |
35 | 35 | ||
36 | while (*new) { | 36 | while (*new) { |
@@ -42,14 +42,40 @@ static void insert(struct test_node *node, struct rb_root *root) | |||
42 | } | 42 | } |
43 | 43 | ||
44 | rb_link_node(&node->rb, parent, new); | 44 | rb_link_node(&node->rb, parent, new); |
45 | rb_insert_color(&node->rb, root); | 45 | rb_insert_color(&node->rb, &root->rb_root); |
46 | } | 46 | } |
47 | 47 | ||
48 | static inline void erase(struct test_node *node, struct rb_root *root) | 48 | static void insert_cached(struct test_node *node, struct rb_root_cached *root) |
49 | { | 49 | { |
50 | rb_erase(&node->rb, root); | 50 | struct rb_node **new = &root->rb_root.rb_node, *parent = NULL; |
51 | u32 key = node->key; | ||
52 | bool leftmost = true; | ||
53 | |||
54 | while (*new) { | ||
55 | parent = *new; | ||
56 | if (key < rb_entry(parent, struct test_node, rb)->key) | ||
57 | new = &parent->rb_left; | ||
58 | else { | ||
59 | new = &parent->rb_right; | ||
60 | leftmost = false; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | rb_link_node(&node->rb, parent, new); | ||
65 | rb_insert_color_cached(&node->rb, root, leftmost); | ||
51 | } | 66 | } |
52 | 67 | ||
68 | static inline void erase(struct test_node *node, struct rb_root_cached *root) | ||
69 | { | ||
70 | rb_erase(&node->rb, &root->rb_root); | ||
71 | } | ||
72 | |||
73 | static inline void erase_cached(struct test_node *node, struct rb_root_cached *root) | ||
74 | { | ||
75 | rb_erase_cached(&node->rb, root); | ||
76 | } | ||
77 | |||
78 | |||
53 | static inline u32 augment_recompute(struct test_node *node) | 79 | static inline u32 augment_recompute(struct test_node *node) |
54 | { | 80 | { |
55 | u32 max = node->val, child_augmented; | 81 | u32 max = node->val, child_augmented; |
@@ -71,9 +97,10 @@ static inline u32 augment_recompute(struct test_node *node) | |||
71 | RB_DECLARE_CALLBACKS(static, augment_callbacks, struct test_node, rb, | 97 | RB_DECLARE_CALLBACKS(static, augment_callbacks, struct test_node, rb, |
72 | u32, augmented, augment_recompute) | 98 | u32, augmented, augment_recompute) |
73 | 99 | ||
74 | static void insert_augmented(struct test_node *node, struct rb_root *root) | 100 | static void insert_augmented(struct test_node *node, |
101 | struct rb_root_cached *root) | ||
75 | { | 102 | { |
76 | struct rb_node **new = &root->rb_node, *rb_parent = NULL; | 103 | struct rb_node **new = &root->rb_root.rb_node, *rb_parent = NULL; |
77 | u32 key = node->key; | 104 | u32 key = node->key; |
78 | u32 val = node->val; | 105 | u32 val = node->val; |
79 | struct test_node *parent; | 106 | struct test_node *parent; |
@@ -91,12 +118,47 @@ static void insert_augmented(struct test_node *node, struct rb_root *root) | |||
91 | 118 | ||
92 | node->augmented = val; | 119 | node->augmented = val; |
93 | rb_link_node(&node->rb, rb_parent, new); | 120 | rb_link_node(&node->rb, rb_parent, new); |
94 | rb_insert_augmented(&node->rb, root, &augment_callbacks); | 121 | rb_insert_augmented(&node->rb, &root->rb_root, &augment_callbacks); |
122 | } | ||
123 | |||
124 | static void insert_augmented_cached(struct test_node *node, | ||
125 | struct rb_root_cached *root) | ||
126 | { | ||
127 | struct rb_node **new = &root->rb_root.rb_node, *rb_parent = NULL; | ||
128 | u32 key = node->key; | ||
129 | u32 val = node->val; | ||
130 | struct test_node *parent; | ||
131 | bool leftmost = true; | ||
132 | |||
133 | while (*new) { | ||
134 | rb_parent = *new; | ||
135 | parent = rb_entry(rb_parent, struct test_node, rb); | ||
136 | if (parent->augmented < val) | ||
137 | parent->augmented = val; | ||
138 | if (key < parent->key) | ||
139 | new = &parent->rb.rb_left; | ||
140 | else { | ||
141 | new = &parent->rb.rb_right; | ||
142 | leftmost = false; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | node->augmented = val; | ||
147 | rb_link_node(&node->rb, rb_parent, new); | ||
148 | rb_insert_augmented_cached(&node->rb, root, | ||
149 | leftmost, &augment_callbacks); | ||
150 | } | ||
151 | |||
152 | |||
153 | static void erase_augmented(struct test_node *node, struct rb_root_cached *root) | ||
154 | { | ||
155 | rb_erase_augmented(&node->rb, &root->rb_root, &augment_callbacks); | ||
95 | } | 156 | } |
96 | 157 | ||
97 | static void erase_augmented(struct test_node *node, struct rb_root *root) | 158 | static void erase_augmented_cached(struct test_node *node, |
159 | struct rb_root_cached *root) | ||
98 | { | 160 | { |
99 | rb_erase_augmented(&node->rb, root, &augment_callbacks); | 161 | rb_erase_augmented_cached(&node->rb, root, &augment_callbacks); |
100 | } | 162 | } |
101 | 163 | ||
102 | static void init(void) | 164 | static void init(void) |
@@ -125,7 +187,7 @@ static void check_postorder_foreach(int nr_nodes) | |||
125 | { | 187 | { |
126 | struct test_node *cur, *n; | 188 | struct test_node *cur, *n; |
127 | int count = 0; | 189 | int count = 0; |
128 | rbtree_postorder_for_each_entry_safe(cur, n, &root, rb) | 190 | rbtree_postorder_for_each_entry_safe(cur, n, &root.rb_root, rb) |
129 | count++; | 191 | count++; |
130 | 192 | ||
131 | WARN_ON_ONCE(count != nr_nodes); | 193 | WARN_ON_ONCE(count != nr_nodes); |
@@ -135,7 +197,7 @@ static void check_postorder(int nr_nodes) | |||
135 | { | 197 | { |
136 | struct rb_node *rb; | 198 | struct rb_node *rb; |
137 | int count = 0; | 199 | int count = 0; |
138 | for (rb = rb_first_postorder(&root); rb; rb = rb_next_postorder(rb)) | 200 | for (rb = rb_first_postorder(&root.rb_root); rb; rb = rb_next_postorder(rb)) |
139 | count++; | 201 | count++; |
140 | 202 | ||
141 | WARN_ON_ONCE(count != nr_nodes); | 203 | WARN_ON_ONCE(count != nr_nodes); |
@@ -147,7 +209,7 @@ static void check(int nr_nodes) | |||
147 | int count = 0, blacks = 0; | 209 | int count = 0, blacks = 0; |
148 | u32 prev_key = 0; | 210 | u32 prev_key = 0; |
149 | 211 | ||
150 | for (rb = rb_first(&root); rb; rb = rb_next(rb)) { | 212 | for (rb = rb_first(&root.rb_root); rb; rb = rb_next(rb)) { |
151 | struct test_node *node = rb_entry(rb, struct test_node, rb); | 213 | struct test_node *node = rb_entry(rb, struct test_node, rb); |
152 | WARN_ON_ONCE(node->key < prev_key); | 214 | WARN_ON_ONCE(node->key < prev_key); |
153 | WARN_ON_ONCE(is_red(rb) && | 215 | WARN_ON_ONCE(is_red(rb) && |
@@ -162,7 +224,7 @@ static void check(int nr_nodes) | |||
162 | } | 224 | } |
163 | 225 | ||
164 | WARN_ON_ONCE(count != nr_nodes); | 226 | WARN_ON_ONCE(count != nr_nodes); |
165 | WARN_ON_ONCE(count < (1 << black_path_count(rb_last(&root))) - 1); | 227 | WARN_ON_ONCE(count < (1 << black_path_count(rb_last(&root.rb_root))) - 1); |
166 | 228 | ||
167 | check_postorder(nr_nodes); | 229 | check_postorder(nr_nodes); |
168 | check_postorder_foreach(nr_nodes); | 230 | check_postorder_foreach(nr_nodes); |
@@ -173,7 +235,7 @@ static void check_augmented(int nr_nodes) | |||
173 | struct rb_node *rb; | 235 | struct rb_node *rb; |
174 | 236 | ||
175 | check(nr_nodes); | 237 | check(nr_nodes); |
176 | for (rb = rb_first(&root); rb; rb = rb_next(rb)) { | 238 | for (rb = rb_first(&root.rb_root); rb; rb = rb_next(rb)) { |
177 | struct test_node *node = rb_entry(rb, struct test_node, rb); | 239 | struct test_node *node = rb_entry(rb, struct test_node, rb); |
178 | WARN_ON_ONCE(node->augmented != augment_recompute(node)); | 240 | WARN_ON_ONCE(node->augmented != augment_recompute(node)); |
179 | } | 241 | } |
@@ -207,7 +269,24 @@ static int __init rbtree_test_init(void) | |||
207 | time = time2 - time1; | 269 | time = time2 - time1; |
208 | 270 | ||
209 | time = div_u64(time, perf_loops); | 271 | time = div_u64(time, perf_loops); |
210 | printk(" -> test 1 (latency of nnodes insert+delete): %llu cycles\n", (unsigned long long)time); | 272 | printk(" -> test 1 (latency of nnodes insert+delete): %llu cycles\n", |
273 | (unsigned long long)time); | ||
274 | |||
275 | time1 = get_cycles(); | ||
276 | |||
277 | for (i = 0; i < perf_loops; i++) { | ||
278 | for (j = 0; j < nnodes; j++) | ||
279 | insert_cached(nodes + j, &root); | ||
280 | for (j = 0; j < nnodes; j++) | ||
281 | erase_cached(nodes + j, &root); | ||
282 | } | ||
283 | |||
284 | time2 = get_cycles(); | ||
285 | time = time2 - time1; | ||
286 | |||
287 | time = div_u64(time, perf_loops); | ||
288 | printk(" -> test 2 (latency of nnodes cached insert+delete): %llu cycles\n", | ||
289 | (unsigned long long)time); | ||
211 | 290 | ||
212 | for (i = 0; i < nnodes; i++) | 291 | for (i = 0; i < nnodes; i++) |
213 | insert(nodes + i, &root); | 292 | insert(nodes + i, &root); |
@@ -215,7 +294,7 @@ static int __init rbtree_test_init(void) | |||
215 | time1 = get_cycles(); | 294 | time1 = get_cycles(); |
216 | 295 | ||
217 | for (i = 0; i < perf_loops; i++) { | 296 | for (i = 0; i < perf_loops; i++) { |
218 | for (node = rb_first(&root); node; node = rb_next(node)) | 297 | for (node = rb_first(&root.rb_root); node; node = rb_next(node)) |
219 | ; | 298 | ; |
220 | } | 299 | } |
221 | 300 | ||
@@ -223,7 +302,31 @@ static int __init rbtree_test_init(void) | |||
223 | time = time2 - time1; | 302 | time = time2 - time1; |
224 | 303 | ||
225 | time = div_u64(time, perf_loops); | 304 | time = div_u64(time, perf_loops); |
226 | printk(" -> test 2 (latency of inorder traversal): %llu cycles\n", (unsigned long long)time); | 305 | printk(" -> test 3 (latency of inorder traversal): %llu cycles\n", |
306 | (unsigned long long)time); | ||
307 | |||
308 | time1 = get_cycles(); | ||
309 | |||
310 | for (i = 0; i < perf_loops; i++) | ||
311 | node = rb_first(&root.rb_root); | ||
312 | |||
313 | time2 = get_cycles(); | ||
314 | time = time2 - time1; | ||
315 | |||
316 | time = div_u64(time, perf_loops); | ||
317 | printk(" -> test 4 (latency to fetch first node)\n"); | ||
318 | printk(" non-cached: %llu cycles\n", (unsigned long long)time); | ||
319 | |||
320 | time1 = get_cycles(); | ||
321 | |||
322 | for (i = 0; i < perf_loops; i++) | ||
323 | node = rb_first_cached(&root); | ||
324 | |||
325 | time2 = get_cycles(); | ||
326 | time = time2 - time1; | ||
327 | |||
328 | time = div_u64(time, perf_loops); | ||
329 | printk(" cached: %llu cycles\n", (unsigned long long)time); | ||
227 | 330 | ||
228 | for (i = 0; i < nnodes; i++) | 331 | for (i = 0; i < nnodes; i++) |
229 | erase(nodes + i, &root); | 332 | erase(nodes + i, &root); |
@@ -261,6 +364,21 @@ static int __init rbtree_test_init(void) | |||
261 | time = div_u64(time, perf_loops); | 364 | time = div_u64(time, perf_loops); |
262 | printk(" -> test 1 (latency of nnodes insert+delete): %llu cycles\n", (unsigned long long)time); | 365 | printk(" -> test 1 (latency of nnodes insert+delete): %llu cycles\n", (unsigned long long)time); |
263 | 366 | ||
367 | time1 = get_cycles(); | ||
368 | |||
369 | for (i = 0; i < perf_loops; i++) { | ||
370 | for (j = 0; j < nnodes; j++) | ||
371 | insert_augmented_cached(nodes + j, &root); | ||
372 | for (j = 0; j < nnodes; j++) | ||
373 | erase_augmented_cached(nodes + j, &root); | ||
374 | } | ||
375 | |||
376 | time2 = get_cycles(); | ||
377 | time = time2 - time1; | ||
378 | |||
379 | time = div_u64(time, perf_loops); | ||
380 | printk(" -> test 2 (latency of nnodes cached insert+delete): %llu cycles\n", (unsigned long long)time); | ||
381 | |||
264 | for (i = 0; i < check_loops; i++) { | 382 | for (i = 0; i < check_loops; i++) { |
265 | init(); | 383 | init(); |
266 | for (j = 0; j < nnodes; j++) { | 384 | for (j = 0; j < nnodes; j++) { |