LWN.net Logo

Generic red-black trees

By Jonathan Corbet
June 5, 2012
Red-black trees (or "rbtrees") are used throughout the kernel to maintain a sorted list of related items. For example, the block I/O subsystem uses an rbtree to maintain a list of outstanding requests sorted by sector number, and the scheduler has an rbtree of runnable tasks sorted by a quantity known as "virtual runtime." The rbtree interface was described in this 2006 article; it has since been extended, but the core features of the API remain the same. In particular, users must provide their own functions for inserting nodes into the tree and performing searches; that allows the creation of highly-optimized rbtrees containing arbitrary types of structures.

There is some appeal to being able to hand-code the search and insertion functions, but there would also be value in generic implementations. The amount of code in the kernel would shrink slightly and the task of debugging those functions would, hopefully, only have to be done once. So it is arguably surprising that nobody has proposed a generic rbtree implementation for all these years. Just as surprising is the fact that two independent generic implementations surfaced at about the same time.

The simpler of the two can be found in this patch set from Kent Overstreet. In this version, rbtree users are required to provide a function to compare two rbtree nodes with this prototype:

    typedef int (rb_cmp_t) (struct rb_node *l, struct rb_node *r);

With that in place, the following new functions become available:

    int rb_insert(struct rb_root *root, struct rb_node *new, rb_cmp_t cmp);
    void rb_insert_allow_dup(struct rb_root *root, struct rb_node *new,
			     rb_cmp_t cmp);
    struct rb_node *rb_search(struct rb_root *root, struct rb_node *search,
			      rb_cmp_t cmp);
    struct rb_node *rb_greater(struct rb_root *root, struct rb_node *search,
			       rb_cmp_t cmp);

As can be seen from the prototypes, all of these functions deal directly with rb_node structures. Only the comparison function needs to know about what sort of structure the rb_node is embedded within. There is no compile-time mechanism to ensure that the comparison function expects the actual structures found in the tree, but one assumes any errors along those lines will show themselves quickly at run time.

One potential problem is that rb_search() and rb_greater() need to know what is being searched for; that, in turn, requires creating and passing in one of the structures stored in the tree. In some situations, that structure may need to be created on the stack, which is a clear problem if the structure is large. Unfortunately, in the block subsystem example mentioned above, that structure (struct request) is large indeed. Kent has tried to work around this problem by providing inlined versions (called _rb_search() and _rb_greater()) that, with luck, will cause the stack allocation to be optimized away. That depends on all supported versions of the compiler doing the right thing on all architectures, though, which may make some people nervous.

The alternative patch set, posted by Daniel Santos, is significantly more complicated. It can be thought of as a set of tricky preprocessor macros and inline functions that serve as a template for the creation of type-specific rbtree implementations. Here, too, one starts with the creation of a comparison function:

    typedef long (*rb_compare_f)(const void *a, const void *b);

In this case, the comparison function will be passed pointers to the actual key value stored in the rbtree node. One then defines an "rbtree interface" with this daunting macro:

    RB_DEFINE_INTERFACE(prefix, container_type, root_member,
			left_pointer, right_pointer,
			object_type, rbnode_member, key_member,
			flags, comparison_function, augment_func);

Eleven arguments are a lot to keep track of, so it makes sense to discuss them in the context of an example (taken from Daniel's patch). The CPU scheduler defines a type (struct cfs_rq in kernel/sched/sched.h) to represent a run queue; each run queue contains a red-black tree called tasks_timeline. To optimize the location of the highest-priority task to run, the scheduler keeps a pointer to the leftmost tree node in rb_leftmost. The entries in the red-black tree are of type struct sched_entity (defined in <linux/sched.h>); the embedded rb_node structure is called run_node, and the key used to sort the tree is the u64 value vruntime.

To create the scheduler's tree using the generic mechanism, Daniel adds this declaration:

    RB_DEFINE_INTERFACE(
	fair_tree,
	struct cfs_rq, tasks_timeline, rb_leftmost, /* no rightmost */,
	struct sched_entity, run_node, vruntime,
	0, compare_vruntime, 0)

Here, fair_tree is the "prefix" value used to generate the names of the tree functions—see below. The next line describes the structure containing the tree (struct cfs_rq), the name of the tree, and the name of the leftmost pointer used by the scheduler; there is no rightmost pointer (nobody cares about the lowest-priority task), so that parameter is simply left blank. The line after that describes the structure contained within the tree (struct sched_entity), the name of its embedded rb_node structure, and the name of the key value. Finally, no flags are given, compare_vruntime() is the comparison function, and, since this is not an augmented tree, there is no augmented callback function. Yes, it lacks a semicolon—the macro supplies that itself.

The result is a new set of functions like:

    struct sched_entity *fair_tree_insert(struct cfs_rq runqueue, 
					  struct sched_entity *entity);
    void fair_tree_remove(struct cfs_rq runqueue, struct sched_entity *entity);
    struct sched_entity *fair_tree_find(struct cfs_rq runqueue, u64 *key);
    /* ... */

These functions are all defined with the proper type, so the compiler will ensure that they are always called with the proper argument types. Everything is defined as __always_inline, so the implementations will be inlined at the place where they are called. That should eliminate any performance penalty caused by out-of-line implementations (as seen in Kent's patch), but, so far, nobody seems to have tried to measure that penalty.

There has been little in the way of review of these patches in general. They represent different approaches to the task of creating a generic red-black implementation, one emphasizing simplicity while the other emphasizes explicitness and type safety. Which version might be inserted into the mainline kernel—if either goes in—is entirely unclear at this point.


(Log in to post comments)

Generic red-black trees

Posted Jun 7, 2012 8:03 UTC (Thu) by rvfh (subscriber, #31018) [Link]

> typedef int (rb_cmp_t) (struct rb_node *l, struct rb_node *r);

Why not typedef int (rb_cmp_t) (const struct rb_node *l, const struct rb_node *r); ?

Same for search function...

Generic red-black trees

Posted Jun 7, 2012 22:24 UTC (Thu) by ppisa (subscriber, #67307) [Link]

I like the second approach more. But the type of rb_compare_f with void pointers without providing compile time typecheck is quite bad solution.

The other problem is that generation of inlined only function means, that there is generic non inlined function for search which receives pointer to compare function. This function is used for all types for insert, remove, find ... processing and cannot be optimized for given type (if we do not count some future more clever LTO/whole project optimization).

I have thought about these problems many years ago for my projects. The result is an GAVL tree implementation in uLUt library. So I cannot hold myself to send pointer it there

http://ulan.git.sourceforge.net/git/gitweb.cgi?p=ulan/ulut

http://cmp.felk.cvut.cz/~pisa/ulan/gavl.pdf

section:Custom AVL Tree Instances

Generator macros in sources:

GAVL_CUST_NODE_INT_DEC + GAVL_CUST_NODE_INT_IMP

GAVL_CUST_NODE_INT_DEC + GAVL_CUST_NODE_INT_REP_IMP

GAVL_FLES_INT_DEC + GAVL_FLES_INT_IMP

I have already send info about my approach to LKML many years ago.

The code has some disadvantages as well. It is AVL tree based, not R-B. At least from Linux kernel point it is worse. The naming of some functions is little obscure due to long history and extension from initial void * variant which is still available in the uLUt. Code is spread in many of our project and income from naming cleanup has not outweigh the version compatibility problems.

There exists Neal H. Walfield's R-B tree implementation with almost same interface as GAVL. It is directly inspired by my approach. It has been originally intended for Hurd-L4 core project. Sources are available at

http://cvs.savannah.gnu.org/viewvc/hurd/hurd-l4/libhurd-b...

So I believe, that it would worth for authors to look and take some ideas from these projects.

Other library which provides tree components trying to solve similar problem is Rusty Russell's CCAN

http://ccodearchive.net/

Another is Martin Mares's libUCW

http://www.ucw.cz/libucw/

I have seen even more similar attempts to generate type safe tree containers for C programs but I consider uLUt (Neal's) interface as the most safe, resulting in good ration of code size and ability to do tight optimization of compares in search function yet sharing all insert, balance code between different types.

Generic red-black trees

Posted Jun 16, 2012 4:16 UTC (Sat) by daniel.santos (subscriber, #85158) [Link]

Since the start of this, my documentation said to define your compare functions as accepting the actual types of your key pointers, but in version 4, I've solved the problem of the compare function's type not being verified (at least, when using the RB_DEFINE_INTERFACE macro). So it will still call compare as type rb_compare_f from the generic functions, but if the compare function you supply to the macro doesn't match up, it will generate an understandable error or warning. Here, I've given it the wrong return type (line 65 is where the macro is called):

/my/path/grbtest2.c: In function ‘__rb_sanity_check_my_objects_rel’:
/my/path/grbtest2.c:65:1: error: passing argument 1 of ‘__rb_verify_compare_fn_ret’ from incompatible pointer type [-Werror]
In file included from /my/path/grbtest.h:30:0,
                 from /my/path/grbtest2.c:21:
include/linux/rbtree.h:917:20: note: expected ‘long int *’ but argument is of type ‘unsigned int *’
cc1: all warnings being treated as errors

And here, I've defined the compare function as accepting types of const u32 instead of const *u32 (line 57 is the compare function's definition).

/my/path/grbtest2.c: In function ‘__rb_sanity_check_my_objects_rel’:
/my/path/grbtest2.c:65:1: error: passing argument 1 of ‘compare_u32’ makes integer from pointer without a cast [-Werror]
/my/path/grbtest2.c:57:15: note: expected ‘u32’ but argument is of type ‘u32 *’

I've implemented similar type checks for all other parameters as well, each calling a static inline function that tells you what it's checking for, i.e., __rb_verify_root(), __rb_verify_left(), etc. That way, you'll have a better chance at knowing what's actually wrong. In the generated code examinations I've made thus far, I haven't seen any compilers actually emit code for these type checks.

As far as calling compare by pointer, you will be most pleased to learn that as of version 4.6.? (4.6.0 maybe?, definitely in 4.6.2), gcc is now capable of optimizing out a call-by-constant-pointer-to-inline(able)-function. Thus, in gcc 4.6.2, optimization is perfect. I mentioned in my previous post the optimization from 4.1 to 4.5 was "acceptable", by this I mostly mean that gcc 4.5.3 did everything that it should do, except inline the call to the compare function (I don't have as many details from 4.1 to 4.4). In fact, the reason I had to add the __flatten attribute was because without it, neither gcc 4.6.3 nor 4.7.0 would inline the call-by-constant-struct-member-function-pointer (i.e., passing compare in struct rb_relationship), but it would if I passed it directly as a function parameter. I'm still not certain if this is a bug or I hit the max pseudo-instructions and it stopped inlining, but the result is actually smaller & faster code.

But anyway, thanks for these links! I've been studying everything I can find that might give me hints on how to make this better. One qunadry I'm still sorting out about my compare function is dealing with compound keys. Currently, if you have a compound key (that is, two fields that need comparing), you basically choose a key to pass for the key offset, and then in your compare function, use container_of() or rb_entry() to get a pointer to your struct and then access your other key(s). Here's an example for one rbtree in drivers/mtd/ubi/wl.c.

static inline long compare_ubi_wl_entry(const int *a_ec, const int *b_ec) {
       long diff = (long)*a_ec - (long)*b_ec;
       if (!diff) {
               diff = (long)container_of(a_ec, struct ubi_wl_entry, ec)->pnum -
                      (long)container_of(b_ec, struct ubi_wl_entry, ec)->pnum;
       }
       return diff;
}

When you call insert, you will always have a full struct who's key will be examined by pointer. The drawback is that when you call find, you generally don't have a struct sitting around to access its key member and you run into the same issue as with Kent's implementation of needing to stick one on the stack. For this reason, I've been considering allowing the user to specify two different compare functions: one accpeting key pointers, and another accepting struct pointers, optionally using the same compare function for both (this would bloat the rbtree.h source code, but not the generated code).

I also meant to address my choice for choosing the return type of long over int in my last post. I made this decision when working on the fair scheduler, as it seemed to allow for a more optimal compare function than int when you need to compare 64-bit values. When doing this on a 64-bit build arch, you just compare them, but on a 32-bit build, you still have to do a bit more work since you have to fit the comparision result in a 32-bit long. But if I choose int, I would have to do that amount of work on both archs. Personally, I really wish there was a way in C to say "compare these two values, and use CPU's negative & zero flags on these branches", because I don't see gcc optimizing out code like this (on AMD64):

int diff = a > b ? 1 : (a < b ? -1 : 0);
if (diff > 0) {
    // do something
} else if (diff < 0) {
    // do something else
} else {
    // do something else else
}

Maybe I just need to file a gcc bug for this, because in every case, I've seen gcc store the 1, -1 or 0 in a register and compare it to zero later, and unless I missed something, I didn't see any instructions that would change those flags in between.

Generic red-black trees

Posted Jun 16, 2012 11:30 UTC (Sat) by ppisa (subscriber, #67307) [Link]

Hello Daniel, thanks for analysis and integration of ideas into generic RB tree code for the kernel. As for the comare function return value, I have observed same problems with int on GCC and have not found how to guide it to better code. Long is probably good idea for all mainstream Linux kernel target architectures. It would lead to a little worse code for H8S if (default) 16 bit int is used (as in our embedded builds). The -mint32 option is used for the kernel, so no difference in code size is caused by long there.

I like your idea with struct rb_relationship and it is great that GCC can optimize generated code.

As for use of empty macro parameters, it works great with GCC, but I have observed problems with MSVC with this approach for uLUt. The use of dummy comment /*dummy*/ in the place of the empty parameter resolved this issue.

Please, can you send URL to the actual patches version? Is it LKML only or you have some copy on FTP/WWW?

As I follow development of other OSes (RTEMS, sometimes HURD etc.) as well, I can see, that same problem is solved again and again. I would like to point RTEMS people to your implementation. But licensing would be problem there because RTEMS core requires GPL with linking exception (core is directly linked with APPs, GPL boundary is on API (regualar C) functions calls). Similar problem arises when used in LGPL licensed libraries. Do you think, that there is chance to provide this "STL" under more permissive license?

Generic red-black trees

Posted Jun 16, 2012 21:20 UTC (Sat) by daniel.santos (subscriber, #85158) [Link]

Ooh, I didn't even think about 16-bit ints. Thanks for the info on msvc. I did finally discover that empty macro arguments is part of the C99 standard -- no surprise that msvc doesn't fully support it yet, but nice to know that there's a work-around!

I don't have this on any web site right now. fail2ban is screwed up on my machine atm, so I closed all my firewall holes until I get it fixed, otherwise, I would make it available from here. Locally, I need to squash a few commits and amend comments and I can post something somewhere.

As far as GPL w/ link-time exception, I am a believer in that if the business/ethical circumstances warrant it. I have plans for C++11 (w/metaprogramming) game engine toolkit project (if I ever kick it off at all) that will be GPL w/ link-time exception as it's the only way it will get any appreciable commercial support.

Are red-black trees really the best ordered-map mechanism that the kernel hotshots can manage?

Posted Jun 8, 2012 0:25 UTC (Fri) by ncm (subscriber, #165) [Link]

It's appalling that the kernel still uses RB trees in time-critical spots. We were taught in school that (as Risible Hans used to repeat) "Trees are fast!". But on modern hardware, trees have really terrible performance characteristics. They foul caches like nothing else. A re-balancing operation dirties an unconscionable number of cache lines. (For a quick, painless education, google up the explanatory material around Judy trees.) Database designers ended up with what are we used to call "modified B* trees" for reasons that uncannily match our present working-set problems: L1 cache is a lot like core, and RAM behind an oversubscribed bus is a lot like drum, if you squint.

The lesson for us is that to improve lookup performance in the kernel, the first step is to abstract away artifacts that imply a tree: in particular, that it is lousy with pointers. Then, we can drop in different, less binary-tree-ish ordered-map implementations without need to savage all of its user subsystems each time it's improved. Eventually we might settle on a page-aware (or page-oblivious) structure that can be fast on real silicon.

After that, we can begin to relieve abstraction overhead, one user subsystem at a time. Since it's not unusual to spend 60% of map lookup time in pipeline-trashing function-pointer dispatch overhead, getting free of function pointers will be an important step.

Some subsystems might do best with a Stone Age packed-heap structure, instead.

Are red-black trees really the best ordered-map mechanism that the kernel hotshots can manage?

Posted Jun 16, 2012 6:20 UTC (Sat) by daniel.santos (subscriber, #85158) [Link]

Well, that's another advantage to using the big macro in this case. My primary motivation when I started this was to completely eliminate the need for an rbtree user to muck with any of the implementation details. You should treat it as an container. Luckily, I seem to have managed that without incurring a run-time abstraction overhead (I wont absolutely know for certain until I get some good performance tests that compare hand-written search & insert with the generic).

Just remember: abstraction doesn't have to incur a run-time overhead. If done correctly, you can take it out of the compiler. I guess luck doesn't hurt sometimes as well (i.e., having an interface that can be mutated).

Generic red-black trees

Posted Jun 8, 2012 5:15 UTC (Fri) by quotemstr (subscriber, #45331) [Link]

And hacky macros are better than C++ templates or code generaton why, exactly? I'm all for using the C preprocessor, but there comes a time when enough is enough and the best option is metaprogramming.

Generic red-black trees

Posted Jun 8, 2012 8:49 UTC (Fri) by przemoc (subscriber, #67594) [Link]

> And hacky macros are better than C++ templates or code generaton why, exactly?

Who said they are? And it's irrelevant here.

> I'm all for using the C preprocessor, but there comes a time when enough is enough and the best option is metaprogramming.

These CPP macros actually allow some kind of poor metaprogramming. It goes without saying that C++ templates are quite powerful as turing complete system, but...

Check a bit old LKML FAQ, if you have forgotten about the context.

Section 15 - Programming Religion
3. Why don't we rewrite the Linux kernel in C++?
http://www.tux.org/lkml/#s15-3

Generic red-black trees

Posted Jun 14, 2012 3:19 UTC (Thu) by slashdot (guest, #22014) [Link]

C++ templates are obviously the best solution.

If not using C++, the best solution is to put the template body in an unguarded include file.

Then, to instantiate the template, you do something like this:

#define NAME foo
#define T foo_t
#define U bar_t
#include "template_body.h"

(with the header #undefining those macros)

This allows to implement the code without huge multiline macros and horrible trailing continuation backslashes.

Generic red-black trees

Posted Jun 14, 2012 13:49 UTC (Thu) by nix (subscriber, #2304) [Link]

What a coincidence. At the very instant you were typing that comment, I was replacing around a thousand lines of duplicated code using exactly that token-pasting technique (this was related to <elf.h> though, and its annoying mass of types with names differing only in 32-vs-64...)

Some of the hoary old C tricks are truly disgusting, but they do work. The preprocessor is horrible, but if you take it away you have to provide *something*, preferably something less horrible, to replace it (as C++ did, but Java notably did not).

Generic red-black trees

Posted Jun 14, 2012 14:08 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link]

People use dynamic bytecode generation and/or reflection in Java quite often as a replacement for things done with templates/preprocessor.

Generic red-black trees

Posted Jun 14, 2012 16:07 UTC (Thu) by etienne (subscriber, #25256) [Link]

Maybe the nice thing about templates/preprocessor is that the work is done once at compilation time, and not each times the software is started.
Also, preprocessor help you to initialise a lot of things which will then be loaded in memory already initialised, ready to use.
It is not unheard-of to generate a lot of variable/structure and sort them by the "sort" command in Makefile to have a sorted list ready to use, with pointer to next already set by the linker. That initialised memory will also have good cache locality.
Some people may also have tried to use the old "m4" macro language to replace the CPP processor, but it did not stick too nicely.

Generic red-black trees

Posted Jun 16, 2012 4:52 UTC (Sat) by daniel.santos (subscriber, #85158) [Link]

I actually considered this "supermacro" approach (as I've heard it called before). But with 17-ish parameters, it just really, really, really seemed verbose to me. One of my aims is reducing copy, paste & edit source code and while using this technique would likely reduce it, it wouldn't be to the degree that I had hoped. (It would improve backtraces and make #if directives available however.)

#define RB_PREFIX          my_objects
#define RB_CONT_TYPE       struct container
#define RB_CONT_ROOT       tree
#define RB_CONT_LEFT       leftmost
#define RB_CONT_RIGHT      /* none */
#define RB_CONT_COUNT      count
#define RB_OBJ_TYPE        struct object
#define RB_OBJ_NODE        node
#define RB_OBJ_KEY         id
#define RB_ALLOW_DUPES     1
#define RB_INSERT_REPLACES 1
#define RB_COMPARE_FUNC    compare_u32
#define RB_AUGMENTED_FUNC  /* none */
#define RB_INSERT_MOD      static __flatten noinline
#define RB_FIND_MOD        /* defaults */
#define RB_INSERT_NEAR_MOD /* defaults */
#define RB_FIND_NEAR_MOD   /* defaults */
#include <linux/grbtree.h>

vs

/* this is actually version 4's RB_DEFINE_INTERFACE, with 16 arguments */
RB_DEFINE_INTERFACE(
	my_objects,
	struct container, tree, leftmost, /* no right */, count,
	struct object, node, id,
	RB_UNIQUE_KEYS | RB_INSERT_REPLACES, compare_u32, /* no augment */,
	static __flatten noinline,,,)

What's your opinion?

As far as metaprogramming is concerned, if you examine it the code, I think you'll see that this is really as close you can get to "C metaprogramming" with current technologies & standards (minus any tricks I haven't learned yet). The "domain language" is basically what you pass to the RB_DEFINE_INTERFACE macro, weak as that may be. This is stored in a compile-time struct rb_relationship object (which shouldn't even appear in the data section of the object file) and tells the compiler how to instantiate the inline functions. Without leaving the C language completely, I don't see how else to create a better solution.

Generic red-black trees

Posted Jun 26, 2012 13:04 UTC (Tue) by juliank (subscriber, #45896) [Link]

You at least know what the first thing does, the latter one is not really understandable (do you want to remember 16 macro arguments or look them up all the time?)

Generic red-black trees

Posted Jun 16, 2012 2:57 UTC (Sat) by daniel.santos (subscriber, #85158) [Link]

First off, thank you Jonathan for this wonderful write-up! I haven't been getting much feedback in LKML and this is exactly the type of discussion I'm needing. I found it funny too that two implementations popped up at almost the exact same time. Unfortunately, we didn't find out about each other for a few months. To add to this, there's a generic interval tree implementation as well, that may eliminate the need for the augmented function in rbtree.

So I appreciate your explanation of my implementation, as you seem to understand my solution pretty well. However, the main thing I want to address (that wasn't in my last patch set posting) is that you don't actually have to use the ugly macro (and believe me, I truly hate ugly macros, but went this way when I exhausted all other options). My implementation actually consists of two layers written on top of the existing rbtree implementation. The first layer consists of an enum, struct rb_relationship and some inline functions.

enum rb_flags {
RB_HAS_LEFTMOST	   = 0x00000001, /* The container has a struct rb_node *leftmost
				    member that will receive a pointer to the
				    leftmost (smallest) object in the tree that
				    is updated during inserts & deletions */
RB_HAS_RIGHTMOST   = 0x00000002, /* same as above (for right side of tree) */
RB_HAS_COUNT	   = 0x00000004, /* The container has an unsigned 32-bit
				    integer field that will receive updates of
				    the object count in the tree. */
RB_UNIQUE_KEYS	   = 0x00000008, /* the tree contains only unique values. */
RB_INSERT_REPLACES = 0x00000010, /* when set, the rb_insert() will replace a
				    value if it matches the supplied one (valid
				    only when RB_UNIQUE_KEYS is set). */
RB_IS_AUGMENTED	   = 0x00000020, /* is an augmented tree */
};

struct rb_relationship {
	long root_offset;
	long left_offset;
	long right_offset;
	long count_offset;
	long node_offset;
	long key_offset;
	int flags;
	const rb_compare_f compare;
	const rb_augment_f augment;
};

As I described in my patch set, this struct can be thought of as both the DDL (Data Definition Language) of a database and the template parameters of a C++ templatized function since the values are used both to define the relationship between two entities as well as dictate how the inline function will expand. The generic inline functions themselves have very little dependency on the pre-processor, aside from build-time bug checks. Like Kent Overstreet's implementation, these functions accept pointers to rb_root & rb_node structs. As of version 4, which I hope to publish early next week, they are all defined static __always_inline __flatten (more on that later).

struct rb_node *rb_find(
		struct rb_root *root,
		const void *key,
		const struct rb_relationship *rel);
struct rb_node *rb_find_near(
		struct rb_node *from,
		const void *key,
		const struct rb_relationship *rel);
struct rb_node *rb_insert(
		struct rb_root *root,
		struct rb_node *node,
		const struct rb_relationship *rel);
struct rb_node *rb_insert_near(
		struct rb_root *root,
		struct rb_node *start,
		struct rb_node *node,
		const struct rb_relationship *rel);
void rb_remove(
		struct rb_root *root,
		struct rb_node *node,
		const struct rb_relationship *rel);

Finally, there is a smaller macro ("smaller" as in "expanding to far fewer characters") that can be used to create a struct rb_relationship initializer, still with a daunting number of parameters however.

#define RB_RELATIONSHIP(						\
		cont_type, root, left, right, count,			\
		obj_type, node, key,					\
		_flags, _compare, _augment) //...

As with RB_DEFINE_INTERFACE, you leave a parameter empty if you don't need the feature (I changed the _augment parameter to use this method as well for the sake of consistency). Thus, there are a total of three ways you can use this interface, which is left up to the programmer's preferences and needs.

  1. Define your struct rb_relationship by hand and call the generic inline functions directly,
  2. use RB_RELATIONSHIP to initialize your struct rb_relationship variable and call the generic inline functions directly, or
  3. use RB_DEFINE_INTERFACE to define the entire interface (ugly macro, but you get type safety and brevity).

So here's what these first two methods would look like (using version 4):

Method 1:

const static struct rb_relationship my_rel = RB_RELATIONSHIP(
	struct cfs_rq, tasks_timeline, rb_leftmost, /* no rightmost or count */, ,
	struct sched_entity, run_node, vruntime,
	0, compare_vruntime, /* no augment */);

Note that, as with RB_DEFINE_INTERFACE, you don't need to supply the RB_HAS_LEFTMOST flag, since the macro does it for you if the leftmost parameter is non-empty.

Method 2:

const static struct rb_relationship my_rel = {
	.root_offset  = offsetof(struct cfs_rq, tasks_timeline),
	.left_offset  = offsetof(struct cfs_rq, rb_leftmost),
	.right_offset = 0,
	.count_offset = 0,
	.node_offset  = offsetof(struct sched_entity, run_node),
	.key_offset   = offsetof(struct sched_entity, vruntime),
	.flags        = RB_HAS_LEFTMOST,
	.compare      = (const rb_compare_f)compare_vruntime,
	.augment      = 0
};

On the topic of the __flatten function modifier, this expands to __attribute__((flatten)) for gcc 4.1 and later (excepting 4.6.0, which had a bug) and was necessary to solve an optimization problem in gcc 4.5. Overall, optimization is perfect in 4.6.3+, acceptable in 4.1.2 to 4.5.3 and not so very pretty in 3.4.6 (haven't tested 4.0 yet). I've just finished the find_near and insert_near (the later is still untested) and I'm working on a test module both to test for correctness and do performance profiling -- something that I see as crucial since the the implementation depends upon the compiler in a somewhat "bleeding edge" fashion. (As an example, an early iteration of this bombed so bad on 4.5 that it produced 3x larger and 2x slower function than on 4.6.2.)

Copyright © 2012, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds