315
the pool. For this reason, if an object contains a reference to another persistent object,
for example, to build a linked data structure, the reference must be an OID and not a
memory address.
The atomic and transactional APIs are built using a combination of the persistent
memory allocator and unified logs. The simplest public interface is the atomic API which
runs a single allocator operation in a unified log context. That log context is not exposed
externally and is created, initialized, and destroyed within a single function call.
The most general-purpose interface is the transactional API, which is based on a
combination of undo logging for snapshots and redo logging for memory allocation and
deallocation. This API has ACID (atomicity, consistency, isolation, durability) properties,
and it is a relatively thin layer that combines the utility of unified logs and the persistent
memory allocator.
For specific transactional use cases that need low-level access to the persistent
memory allocator, there is an “action” API. The action API is essentially a pass-through
to the raw memory allocator interface, alongside helpers for usability. This API can
be leveraged to create low-overhead algorithms that issue fewer memory fences, as
compared to general-purpose transactions, at the cost of ease of use.
All public interfaces produce and operate on PMEMoids as a replacement for
pointers. This comes with space overhead because PMEMoids are 16 bytes. There is
also a performance overhead for the translation to a normal pointer. The upside is that
objects can be safely referenced between different instances of the application and even
different persistent memory pools.
The pool management API opens, maps, and manages persistent memory resident
files or devices. This is where the replication is configured, metadata and the heap are
initialized, and all the runtime data is created. This is also where the crucial recovery of
interrupted transactions happens. Once recovery is complete, all prior transactions are
either committed or aborted, the persistent state is consistent, and the logs are clean and
ready to be used again.
The Uncertainty of Memory Mapping: Persistent
Memory Object Identifier
A key concept that is important for any persistent memory application is how to
represent the relative position of an object within a pool of memory, and even beyond
it. That is, how do you implement pointers? You could rely on normal pointers, which
Chapter 16 pMDK Internals: IMportant algorIthMs anD Data struCtures
the pool. For this reason, if an object contains a reference to another persistent object,
for example, to build a linked data structure, the reference must be an OID and not a
memory address.
The atomic and transactional APIs are built using a combination of the persistent
memory allocator and unified logs. The simplest public interface is the atomic API which
runs a single allocator operation in a unified log context. That log context is not exposed
externally and is created, initialized, and destroyed within a single function call.
The most general-purpose interface is the transactional API, which is based on a
combination of undo logging for snapshots and redo logging for memory allocation and
deallocation. This API has ACID (atomicity, consistency, isolation, durability) properties,
and it is a relatively thin layer that combines the utility of unified logs and the persistent
memory allocator.
For specific transactional use cases that need low-level access to the persistent
memory allocator, there is an “action” API. The action API is essentially a pass-through
to the raw memory allocator interface, alongside helpers for usability. This API can
be leveraged to create low-overhead algorithms that issue fewer memory fences, as
compared to general-purpose transactions, at the cost of ease of use.
All public interfaces produce and operate on PMEMoids as a replacement for
pointers. This comes with space overhead because PMEMoids are 16 bytes. There is
also a performance overhead for the translation to a normal pointer. The upside is that
objects can be safely referenced between different instances of the application and even
different persistent memory pools.
The pool management API opens, maps, and manages persistent memory resident
files or devices. This is where the replication is configured, metadata and the heap are
initialized, and all the runtime data is created. This is also where the crucial recovery of
interrupted transactions happens. Once recovery is complete, all prior transactions are
either committed or aborted, the persistent state is consistent, and the logs are clean and
ready to be used again.
The Uncertainty of Memory Mapping: Persistent
Memory Object Identifier
A key concept that is important for any persistent memory application is how to
represent the relative position of an object within a pool of memory, and even beyond
it. That is, how do you implement pointers? You could rely on normal pointers, which
Chapter 16 pMDK Internals: IMportant algorIthMs anD Data struCtures
