summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavidlohr Bueso <dave@stgolabs.net>2017-09-08 19:14:36 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-09-08 21:26:48 -0400
commitcd9e61ed1eebbcd5dfad59475d41ec58d9b64b6a (patch)
treec1d649fce5c7a7be138856e172c2304424c1a2e9
parentc32ee3d9abd284b4fcaacc250b101f93829c7bae (diff)
rbtree: cache leftmost node internally
Patch series "rbtree: Cache leftmost node internally", v4. A series to extending rbtrees to internally cache the leftmost node such that we can have fast overlap check optimization for all interval tree users[1]. The benefits of this series are that: (i) Unify users that do internal leftmost node caching. (ii) Optimize all interval tree users. (iii) Convert at least two new users (epoll and procfs) to the new interface. This patch (of 16): Red-black tree semantics imply that nodes with smaller or greater (or equal for duplicates) keys always be to the left and right, respectively. For the kernel this is extremely evident when considering our rb_first() semantics. Enabling lookups for the smallest node in the tree in O(1) can save a good chunk of cycles in not having to walk down the tree each time. To this end there are a few core users that explicitly do this, such as the scheduler and rtmutexes. There is also the desire for interval trees to have this optimization allowing faster overlap checking. This patch introduces a new 'struct rb_root_cached' which is just the root with a cached pointer to the leftmost node. The reason why the regular rb_root was not extended instead of adding a new structure was that this allows the user to have the choice between memory footprint and actual tree performance. The new wrappers on top of the regular rb_root calls are: - rb_first_cached(cached_root) -- which is a fast replacement for rb_first. - rb_insert_color_cached(node, cached_root, new) - rb_erase_cached(node, cached_root) In addition, augmented cached interfaces are also added for basic insertion and deletion operations; which becomes important for the interval tree changes. With the exception of the inserts, which adds a bool for updating the new leftmost, the interfaces are kept the same. To this end, porting rb users to the cached version becomes really trivial, and keeping current rbtree semantics for users that don't care about the optimization requires zero overhead. Link: http://lkml.kernel.org/r/20170719014603.19029-2-dave@stgolabs.net Signed-off-by: Davidlohr Bueso <dbueso@suse.de> Reviewed-by: Jan Kara <jack@suse.cz> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--Documentation/rbtree.txt33
-rw-r--r--include/linux/rbtree.h21
-rw-r--r--include/linux/rbtree_augmented.h33
-rw-r--r--lib/rbtree.c34
4 files changed, 113 insertions, 8 deletions
diff --git a/Documentation/rbtree.txt b/Documentation/rbtree.txt
index b8a8c70b0188..c42a21b99046 100644
--- a/Documentation/rbtree.txt
+++ b/Documentation/rbtree.txt
@@ -193,6 +193,39 @@ Example::
193 for (node = rb_first(&mytree); node; node = rb_next(node)) 193 for (node = rb_first(&mytree); node; node = rb_next(node))
194 printk("key=%s\n", rb_entry(node, struct mytype, node)->keystring); 194 printk("key=%s\n", rb_entry(node, struct mytype, node)->keystring);
195 195
196Cached rbtrees
197--------------
198
199Computing the leftmost (smallest) node is quite a common task for binary
200search trees, such as for traversals or users relying on a the particular
201order for their own logic. To this end, users can use 'struct rb_root_cached'
202to optimize O(logN) rb_first() calls to a simple pointer fetch avoiding
203potentially expensive tree iterations. This is done at negligible runtime
204overhead for maintanence; albeit larger memory footprint.
205
206Similar to the rb_root structure, cached rbtrees are initialized to be
207empty via:
208
209 struct rb_root_cached mytree = RB_ROOT_CACHED;
210
211Cached rbtree is simply a regular rb_root with an extra pointer to cache the
212leftmost node. This allows rb_root_cached to exist wherever rb_root does,
213which permits augmented trees to be supported as well as only a few extra
214interfaces:
215
216 struct rb_node *rb_first_cached(struct rb_root_cached *tree);
217 void rb_insert_color_cached(struct rb_node *, struct rb_root_cached *, bool);
218 void rb_erase_cached(struct rb_node *node, struct rb_root_cached *);
219
220Both insert and erase calls have their respective counterpart of augmented
221trees:
222
223 void rb_insert_augmented_cached(struct rb_node *node, struct rb_root_cached *,
224 bool, struct rb_augment_callbacks *);
225 void rb_erase_augmented_cached(struct rb_node *, struct rb_root_cached *,
226 struct rb_augment_callbacks *);
227
228
196Support for Augmented rbtrees 229Support for Augmented rbtrees
197----------------------------- 230-----------------------------
198 231
diff --git a/include/linux/rbtree.h b/include/linux/rbtree.h
index e585018498d5..d574361943ea 100644
--- a/include/linux/rbtree.h
+++ b/include/linux/rbtree.h
@@ -44,10 +44,25 @@ struct rb_root {
44 struct rb_node *rb_node; 44 struct rb_node *rb_node;
45}; 45};
46 46
47/*
48 * Leftmost-cached rbtrees.
49 *
50 * We do not cache the rightmost node based on footprint
51 * size vs number of potential users that could benefit
52 * from O(1) rb_last(). Just not worth it, users that want
53 * this feature can always implement the logic explicitly.
54 * Furthermore, users that want to cache both pointers may
55 * find it a bit asymmetric, but that's ok.
56 */
57struct rb_root_cached {
58 struct rb_root rb_root;
59 struct rb_node *rb_leftmost;
60};
47 61
48#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3)) 62#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3))
49 63
50#define RB_ROOT (struct rb_root) { NULL, } 64#define RB_ROOT (struct rb_root) { NULL, }
65#define RB_ROOT_CACHED (struct rb_root_cached) { {NULL, }, NULL }
51#define rb_entry(ptr, type, member) container_of(ptr, type, member) 66#define rb_entry(ptr, type, member) container_of(ptr, type, member)
52 67
53#define RB_EMPTY_ROOT(root) (READ_ONCE((root)->rb_node) == NULL) 68#define RB_EMPTY_ROOT(root) (READ_ONCE((root)->rb_node) == NULL)
@@ -69,6 +84,12 @@ extern struct rb_node *rb_prev(const struct rb_node *);
69extern struct rb_node *rb_first(const struct rb_root *); 84extern struct rb_node *rb_first(const struct rb_root *);
70extern struct rb_node *rb_last(const struct rb_root *); 85extern struct rb_node *rb_last(const struct rb_root *);
71 86
87extern void rb_insert_color_cached(struct rb_node *,
88 struct rb_root_cached *, bool);
89extern void rb_erase_cached(struct rb_node *node, struct rb_root_cached *);
90/* Same as rb_first(), but O(1) */
91#define rb_first_cached(root) (root)->rb_leftmost
92
72/* Postorder iteration - always visit the parent after its children */ 93/* Postorder iteration - always visit the parent after its children */
73extern struct rb_node *rb_first_postorder(const struct rb_root *); 94extern struct rb_node *rb_first_postorder(const struct rb_root *);
74extern struct rb_node *rb_next_postorder(const struct rb_node *); 95extern struct rb_node *rb_next_postorder(const struct rb_node *);
diff --git a/include/linux/rbtree_augmented.h b/include/linux/rbtree_augmented.h
index 9702b6e183bc..6bfd2b581f75 100644
--- a/include/linux/rbtree_augmented.h
+++ b/include/linux/rbtree_augmented.h
@@ -41,7 +41,9 @@ struct rb_augment_callbacks {
41 void (*rotate)(struct rb_node *old, struct rb_node *new); 41 void (*rotate)(struct rb_node *old, struct rb_node *new);
42}; 42};
43 43
44extern void __rb_insert_augmented(struct rb_node *node, struct rb_root *root, 44extern void __rb_insert_augmented(struct rb_node *node,
45 struct rb_root *root,
46 bool newleft, struct rb_node **leftmost,
45 void (*augment_rotate)(struct rb_node *old, struct rb_node *new)); 47 void (*augment_rotate)(struct rb_node *old, struct rb_node *new));
46/* 48/*
47 * Fixup the rbtree and update the augmented information when rebalancing. 49 * Fixup the rbtree and update the augmented information when rebalancing.
@@ -57,7 +59,16 @@ static inline void
57rb_insert_augmented(struct rb_node *node, struct rb_root *root, 59rb_insert_augmented(struct rb_node *node, struct rb_root *root,
58 const struct rb_augment_callbacks *augment) 60 const struct rb_augment_callbacks *augment)
59{ 61{
60 __rb_insert_augmented(node, root, augment->rotate); 62 __rb_insert_augmented(node, root, false, NULL, augment->rotate);
63}
64
65static inline void
66rb_insert_augmented_cached(struct rb_node *node,
67 struct rb_root_cached *root, bool newleft,
68 const struct rb_augment_callbacks *augment)
69{
70 __rb_insert_augmented(node, &root->rb_root,
71 newleft, &root->rb_leftmost, augment->rotate);
61} 72}
62 73
63#define RB_DECLARE_CALLBACKS(rbstatic, rbname, rbstruct, rbfield, \ 74#define RB_DECLARE_CALLBACKS(rbstatic, rbname, rbstruct, rbfield, \
@@ -150,6 +161,7 @@ extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root,
150 161
151static __always_inline struct rb_node * 162static __always_inline struct rb_node *
152__rb_erase_augmented(struct rb_node *node, struct rb_root *root, 163__rb_erase_augmented(struct rb_node *node, struct rb_root *root,
164 struct rb_node **leftmost,
153 const struct rb_augment_callbacks *augment) 165 const struct rb_augment_callbacks *augment)
154{ 166{
155 struct rb_node *child = node->rb_right; 167 struct rb_node *child = node->rb_right;
@@ -157,6 +169,9 @@ __rb_erase_augmented(struct rb_node *node, struct rb_root *root,
157 struct rb_node *parent, *rebalance; 169 struct rb_node *parent, *rebalance;
158 unsigned long pc; 170 unsigned long pc;
159 171
172 if (leftmost && node == *leftmost)
173 *leftmost = rb_next(node);
174
160 if (!tmp) { 175 if (!tmp) {
161 /* 176 /*
162 * Case 1: node to erase has no more than 1 child (easy!) 177 * Case 1: node to erase has no more than 1 child (easy!)
@@ -256,9 +271,21 @@ static __always_inline void
256rb_erase_augmented(struct rb_node *node, struct rb_root *root, 271rb_erase_augmented(struct rb_node *node, struct rb_root *root,
257 const struct rb_augment_callbacks *augment) 272 const struct rb_augment_callbacks *augment)
258{ 273{
259 struct rb_node *rebalance = __rb_erase_augmented(node, root, augment); 274 struct rb_node *rebalance = __rb_erase_augmented(node, root,
275 NULL, augment);
260 if (rebalance) 276 if (rebalance)
261 __rb_erase_color(rebalance, root, augment->rotate); 277 __rb_erase_color(rebalance, root, augment->rotate);
262} 278}
263 279
280static __always_inline void
281rb_erase_augmented_cached(struct rb_node *node, struct rb_root_cached *root,
282 const struct rb_augment_callbacks *augment)
283{
284 struct rb_node *rebalance = __rb_erase_augmented(node, &root->rb_root,
285 &root->rb_leftmost,
286 augment);
287 if (rebalance)
288 __rb_erase_color(rebalance, &root->rb_root, augment->rotate);
289}
290
264#endif /* _LINUX_RBTREE_AUGMENTED_H */ 291#endif /* _LINUX_RBTREE_AUGMENTED_H */
diff --git a/lib/rbtree.c b/lib/rbtree.c
index 4ba2828a67c0..d102d9d2ffaa 100644
--- a/lib/rbtree.c
+++ b/lib/rbtree.c
@@ -95,10 +95,14 @@ __rb_rotate_set_parents(struct rb_node *old, struct rb_node *new,
95 95
96static __always_inline void 96static __always_inline void
97__rb_insert(struct rb_node *node, struct rb_root *root, 97__rb_insert(struct rb_node *node, struct rb_root *root,
98 bool newleft, struct rb_node **leftmost,
98 void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) 99 void (*augment_rotate)(struct rb_node *old, struct rb_node *new))
99{ 100{
100 struct rb_node *parent = rb_red_parent(node), *gparent, *tmp; 101 struct rb_node *parent = rb_red_parent(node), *gparent, *tmp;
101 102
103 if (newleft)
104 *leftmost = node;
105
102 while (true) { 106 while (true) {
103 /* 107 /*
104 * Loop invariant: node is red 108 * Loop invariant: node is red
@@ -434,19 +438,38 @@ static const struct rb_augment_callbacks dummy_callbacks = {
434 438
435void rb_insert_color(struct rb_node *node, struct rb_root *root) 439void rb_insert_color(struct rb_node *node, struct rb_root *root)
436{ 440{
437 __rb_insert(node, root, dummy_rotate); 441 __rb_insert(node, root, false, NULL, dummy_rotate);
438} 442}
439EXPORT_SYMBOL(rb_insert_color); 443EXPORT_SYMBOL(rb_insert_color);
440 444
441void rb_erase(struct rb_node *node, struct rb_root *root) 445void rb_erase(struct rb_node *node, struct rb_root *root)
442{ 446{
443 struct rb_node *rebalance; 447 struct rb_node *rebalance;
444 rebalance = __rb_erase_augmented(node, root, &dummy_callbacks); 448 rebalance = __rb_erase_augmented(node, root,
449 NULL, &dummy_callbacks);
445 if (rebalance) 450 if (rebalance)
446 ____rb_erase_color(rebalance, root, dummy_rotate); 451 ____rb_erase_color(rebalance, root, dummy_rotate);
447} 452}
448EXPORT_SYMBOL(rb_erase); 453EXPORT_SYMBOL(rb_erase);
449 454
455void rb_insert_color_cached(struct rb_node *node,
456 struct rb_root_cached *root, bool leftmost)
457{
458 __rb_insert(node, &root->rb_root, leftmost,
459 &root->rb_leftmost, dummy_rotate);
460}
461EXPORT_SYMBOL(rb_insert_color_cached);
462
463void rb_erase_cached(struct rb_node *node, struct rb_root_cached *root)
464{
465 struct rb_node *rebalance;
466 rebalance = __rb_erase_augmented(node, &root->rb_root,
467 &root->rb_leftmost, &dummy_callbacks);
468 if (rebalance)
469 ____rb_erase_color(rebalance, &root->rb_root, dummy_rotate);
470}
471EXPORT_SYMBOL(rb_erase_cached);
472
450/* 473/*
451 * Augmented rbtree manipulation functions. 474 * Augmented rbtree manipulation functions.
452 * 475 *
@@ -455,9 +478,10 @@ EXPORT_SYMBOL(rb_erase);
455 */ 478 */
456 479
457void __rb_insert_augmented(struct rb_node *node, struct rb_root *root, 480void __rb_insert_augmented(struct rb_node *node, struct rb_root *root,
481 bool newleft, struct rb_node **leftmost,
458 void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) 482 void (*augment_rotate)(struct rb_node *old, struct rb_node *new))
459{ 483{
460 __rb_insert(node, root, augment_rotate); 484 __rb_insert(node, root, newleft, leftmost, augment_rotate);
461} 485}
462EXPORT_SYMBOL(__rb_insert_augmented); 486EXPORT_SYMBOL(__rb_insert_augmented);
463 487
@@ -502,7 +526,7 @@ struct rb_node *rb_next(const struct rb_node *node)
502 * as we can. 526 * as we can.
503 */ 527 */
504 if (node->rb_right) { 528 if (node->rb_right) {
505 node = node->rb_right; 529 node = node->rb_right;
506 while (node->rb_left) 530 while (node->rb_left)
507 node=node->rb_left; 531 node=node->rb_left;
508 return (struct rb_node *)node; 532 return (struct rb_node *)node;
@@ -534,7 +558,7 @@ struct rb_node *rb_prev(const struct rb_node *node)
534 * as we can. 558 * as we can.
535 */ 559 */
536 if (node->rb_left) { 560 if (node->rb_left) {
537 node = node->rb_left; 561 node = node->rb_left;
538 while (node->rb_right) 562 while (node->rb_right)
539 node=node->rb_right; 563 node=node->rb_right;
540 return (struct rb_node *)node; 564 return (struct rb_node *)node;