LWN.net Logo

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

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)
Parent article: Generic red-black trees

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.


(Log in to post comments)

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).

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