[libc-commits] [libc] [libc] Use best-fit binary trie to make malloc logarithmic (PR #106259)

Daniel Thornburgh via libc-commits libc-commits at lists.llvm.org
Thu Nov 14 11:57:57 PST 2024


================
@@ -0,0 +1,237 @@
+//===-- Interface for freetrie --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
+#define LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
+
+#include "freelist.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+/// A trie of free lists.
+///
+/// This is an unusual little data structure originally from Doug Lea's malloc.
+/// Finding the best fit from a set of differently-sized free list typically
+/// required some kind of ordered map, and these are typically implemented using
+/// a self-balancing binary search tree. Those are notorious for having a
+/// relatively large number of special cases, while this trie has relatively
+/// few, which helps with code size.
+///
+/// Operations on the trie are logarithmic not on the number of nodes within it,
+/// but rather the fixed range of possible sizes that the trie can contain. This
+/// means that the data structure would likely actually perform worse than an
+/// e.g. red-black tree, but its implementation is still much simpler.
+///
+/// Each trie node's children subdivide the range of possible sizes into two
+/// halves: a lower and an upper. The node itself holds a free list of some size
+/// within its range. This makes it possible to summarily replace any node with
+/// any leaf within its subtrie, which makes it very straightforward to remove a
+/// node. Insertion is also simple; the only real complexity lies with finding
+/// the best fit. This can still be done in logarithmic time with only a few
+/// cases to consider.
+///
+/// The trie refers to, but does not own, the Nodes that comprise it.
+class FreeTrie {
+public:
+  /// A trie node that is also a free list. Only the head node of each list is
+  /// actually part of the trie. The subtrie contains a continous SizeRange of
+  /// free lists. The lower and upper subtrie's contain the lower and upper half
+  /// of the subtries range. There is no direct relationship between the size of
+  /// this node's free list and the contents of the lower and upper subtries.
+  class Node : public FreeList::Node {
+    /// The child subtrie covering the lower half of this subtrie's size range.
+    /// Undefined if this is not the head of the list.
+    Node *lower;
+    /// The child subtrie covering the upper half of this subtrie's size range.
+    /// Undefined if this is not the head of the list.
+    Node *upper;
+    /// The parent subtrie. nullptr if this is the root or not the head of the
+    /// list.
+    Node *parent;
+
+    friend class FreeTrie;
+  };
+
+  /// Power-of-two range of sizes covered by a subtrie.
+  struct SizeRange {
+    size_t min;
+    size_t width;
+
+    LIBC_INLINE constexpr SizeRange(size_t min, size_t width)
+        : min(min), width(width) {
+      LIBC_ASSERT(!(width & (width - 1)) && "width must be a power of two");
+    }
+
+    /// @returns The lower half of the size range.
+    LIBC_INLINE SizeRange lower() const { return {min, width / 2}; }
+
+    /// @returns The lower half of the size range.
----------------
mysterymath wrote:

Done, thanks.

https://github.com/llvm/llvm-project/pull/106259


More information about the libc-commits mailing list