-
Notifications
You must be signed in to change notification settings - Fork 1
Memory Allocator
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.
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 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 allocateDrillBuf
s. -
BaseAllocator
: The concrete implementation ofBufferAllocator
. -
RootAllocator
: The top-mostBaseAllocator
. -
ChildAllocator
: Allocators form a tree that parallels the operator tree. This class is a form ofBaseAllocator
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 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 aDrillBuf
by first creating aAllocationManager
to hold the UDLE. - The
AllocationManager
asks for the UDLE from Netty. - The
AllocationManager
creates aBufferLedger
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 theDrillBuf
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.