328
One of the most impactful aspects of persistent memory allocation is how the
memory is provisioned from the operating system. We previously explained that for
normal volatile allocators, the memory is usually acquired through anonymous memory
mappings that are backed by the page cache. In contrast, persistent heaps must use filebased memory mappings, backed directly by persistent memory. The difference might
be subtle, but it has a significant impact on the way the allocator must be designed. The
allocator must manage the entire virtual address space, retain information about any
potential noncontiguous regions of the heap, and avoid excessive overprovisioning of
virtual address space. Volatile allocators can rely on the operating system to coalesce
noncontiguous physical pages into contiguous virtual ones, whereas persistent allocators
cannot do the same without explicit and complicated techniques. Additionally, for some
file system implementations, the allocator cannot assume that the physical memory is
allocated at the time of the first page fault, so it must be conservative with internal block
allocations.
Another problem for allocation from file-based mappings is that of perception.
Normal allocators, due to memory overcommitment, seemingly never run out of memory
because they are allocating the virtual address space, which is effectively infinite. There
are negative performance consequences of address space bloat, and memory allocators
actively try to avoid it, but they are not easily measurable in a typical application. In
contrast, memory heaps allocate from a finite resource, the persistent memory device, or
a file. This exacerbates the common phenomenon that is heap fragmentation by making
it trivially measurable, creating the perception that persistent memory allocators are
less efficient than volatile ones. They can be, but the operating system does a lot of work
behind the scene to hide fragmentation of traditional memory allocators.
ACID Transactions: Efficient Low-Level Persistent
Transactions
The four components we just described – lanes, redo logs, undo logs, and the
transactional memory allocator – form the basis of libpmemobj's implementation of
ACID transactions that we defined in Chapter 4.
A transaction’s persistent state consists of three logs. First is an undo log, which
contains snapshots of user data. Second is an external redo log, which contains
allocations and deallocations performed by the user. Third is an internal redo log, which
is used to perform atomic metadata allocations and deallocations. This is technically not
Chapter 16 pMDK Internals: IMportant algorIthMs anD Data struCtures
One of the most impactful aspects of persistent memory allocation is how the
memory is provisioned from the operating system. We previously explained that for
normal volatile allocators, the memory is usually acquired through anonymous memory
mappings that are backed by the page cache. In contrast, persistent heaps must use filebased memory mappings, backed directly by persistent memory. The difference might
be subtle, but it has a significant impact on the way the allocator must be designed. The
allocator must manage the entire virtual address space, retain information about any
potential noncontiguous regions of the heap, and avoid excessive overprovisioning of
virtual address space. Volatile allocators can rely on the operating system to coalesce
noncontiguous physical pages into contiguous virtual ones, whereas persistent allocators
cannot do the same without explicit and complicated techniques. Additionally, for some
file system implementations, the allocator cannot assume that the physical memory is
allocated at the time of the first page fault, so it must be conservative with internal block
allocations.
Another problem for allocation from file-based mappings is that of perception.
Normal allocators, due to memory overcommitment, seemingly never run out of memory
because they are allocating the virtual address space, which is effectively infinite. There
are negative performance consequences of address space bloat, and memory allocators
actively try to avoid it, but they are not easily measurable in a typical application. In
contrast, memory heaps allocate from a finite resource, the persistent memory device, or
a file. This exacerbates the common phenomenon that is heap fragmentation by making
it trivially measurable, creating the perception that persistent memory allocators are
less efficient than volatile ones. They can be, but the operating system does a lot of work
behind the scene to hide fragmentation of traditional memory allocators.
ACID Transactions: Efficient Low-Level Persistent
Transactions
The four components we just described – lanes, redo logs, undo logs, and the
transactional memory allocator – form the basis of libpmemobj's implementation of
ACID transactions that we defined in Chapter 4.
A transaction’s persistent state consists of three logs. First is an undo log, which
contains snapshots of user data. Second is an external redo log, which contains
allocations and deallocations performed by the user. Third is an internal redo log, which
is used to perform atomic metadata allocations and deallocations. This is technically not
Chapter 16 pMDK Internals: IMportant algorIthMs anD Data struCtures
