Skip to content
Paul Rogers edited this page Apr 7, 2017 · 4 revisions

The major components of Drill's memory allocator are:

  • The value vector, which is backed by
  • The DrillBuf, which represents a single logical buffer, backed by
  • The UDLE (unsigned, direct, little-endian) buffer, sliced and shared by multiple DrillBufs
  • The Drill memory allocator, which manages the UDLEs and creates DrillBufs
  • The Netty memory allocator, which handles UDLE allocation from its own internal pool or from
  • Unsafe, the access to the system direct memory pool.

The purpose of this discussion is to describe how these pieces fit together.

DrillBuf

The Drillbuf is Drill's extension to Netty's ByteBuf extension of the JDK NIO's ByteBuffer abstraction. To the rest of Drill, a DrillBuf presents a single buffer of data, of some length.

Drill Memory Allocator

Drill has a very elaborate memory manager that forms the "first like support" for managing memory. Major classes:

  • BufferAllocator: the allocation interface within Drill. Provides an API to allocate DrillBufs.
  • BaseAllocator: The concrete implementation of BufferAllocator.
  • RootAllocator: The top-most BaseAllocator.
  • ChildAllocator: Allocators form a tree that parallels the operator tree. This class is a form of BaseAllocator for non-root allocators.

Allocators have two primary tasks:

  • Provide an accounting of the number of bytes used by the client of the allocator.
  • Perform actual memory allocations and releases.

Allocators are given two parameters: the initial and maximum allocations. Chile allocators keep track of the actual allocation. When the client wishes to allocate more memory than is held locally by the child, the child asks its parent for the extra memory, and so on up the allocator tree. Note that this memory is simply an accounting for memory: not actual memory.

Actual memory is allocated when needed. In general, the amount of memory actually allocated by an allocator is less than or equal to the amount assigned to the allocator. (Accounting amount may exceed physical amount.)

Memory Allocation

Memory allocation is performed by the AllocationManager class. This class holds onto something called the "UDLE" (UnsafeDirectLittleEndian) which is a reference to a block of direct memory provided by Unsafe via Netty.

Now, if Drill had a 1:1 correspondence between Drillbuf and UDLE, things would be simple. But, in fact, a single UDLE can be referenced by one, two or more DrillBuf objects. Each DrillBuf owns a distinct "slice" of the UDLE (with possibly some of the UDLE not allocated to any Drillbuf.)

Given this m:1 relationship between Drillbuf and UDLE, we need a separate mechanism to manage the UDLE. The AllocationManager is that mechanism. The BufferLedger forms the link between Drillbuf and UDLE: it specifies the offset and length of the UDLE assigned to the Drillbuf.

All of this provides the following structure:

  • The BufferAllocator allocates a DrillBuf by first creating a AllocationManager to hold the UDLE.
  • The AllocationManager asks for the UDLE from Netty.
  • The AllocationManager creates a BufferLedger to track the portion of the UDLE to assign to the DrillBuf. Since this is an initial allocation, the ledger owns the entire UDLE.
  • The BufferLedger creates the DrillBuf that is the application interface to the (slice of the) UDLE.

Extensive reference count keeps all the various pieces in sync as Drill creates and releases slices.

Clone this wiki locally