318
libpmemobj uses the most generic approach of the 16-byte offset pointer. This allows
you to make your own choice since all other pointer types can be directly derived from
it. libpmemobj bindings for more expressive languages than C99, such as C++, can also
provide different types of pointers with different trade-offs.
Figure 16-3 shows the translation method used to convert a libpmemobj persistent
pointer, PMEMoid, into a valid C pointer. In principle, this approach is very simple.
We look up the base address of the pool through the pool identifier and then add the
object offset to it. The method itself is static inline and defined in the public header file
for libpmemobj to avoid a function call on every deference. The problem is the lookup
method, which, for an application linked with a dynamic library, means a call to a
different compilation unit, and that might be costly for a very commonly performed
operation. To resolve this problem, the translation method has a per-thread cache
of the last base address, which removes the necessity of calling the lookup with each
dereferencing for the common case where persistent pointers from the same pool are
accessed close together.
The pool lookup method itself is implemented using a radix tree that stores
identifier-address pairs. This tree has a lock-free read operation, which is necessary
because each non-cached pointer translation would otherwise have to acquire a lock to
be thread-safe, and that would have a severe negative performance impact and could
potentially serialize access to persistent memory.
Persistent Thread Local Storage: Using Lanes
Very early in the development of PMDK, we found that persistent memory
programming closely resembles multithreaded programming because it requires
restricting visibility of memory changes – either through locking or transactions – to
other threads or instances of the program. But that is not the only similarity. The
other similarity, which we discuss in this section, is how sometimes low-level code
Figure 16-3. Example of using a PMEMoid in a persistent memory pool
Chapter 16 pMDK Internals: IMportant algorIthMs anD Data struCtures
libpmemobj uses the most generic approach of the 16-byte offset pointer. This allows
you to make your own choice since all other pointer types can be directly derived from
it. libpmemobj bindings for more expressive languages than C99, such as C++, can also
provide different types of pointers with different trade-offs.
Figure 16-3 shows the translation method used to convert a libpmemobj persistent
pointer, PMEMoid, into a valid C pointer. In principle, this approach is very simple.
We look up the base address of the pool through the pool identifier and then add the
object offset to it. The method itself is static inline and defined in the public header file
for libpmemobj to avoid a function call on every deference. The problem is the lookup
method, which, for an application linked with a dynamic library, means a call to a
different compilation unit, and that might be costly for a very commonly performed
operation. To resolve this problem, the translation method has a per-thread cache
of the last base address, which removes the necessity of calling the lookup with each
dereferencing for the common case where persistent pointers from the same pool are
accessed close together.
The pool lookup method itself is implemented using a radix tree that stores
identifier-address pairs. This tree has a lock-free read operation, which is necessary
because each non-cached pointer translation would otherwise have to acquire a lock to
be thread-safe, and that would have a severe negative performance impact and could
potentially serialize access to persistent memory.
Persistent Thread Local Storage: Using Lanes
Very early in the development of PMDK, we found that persistent memory
programming closely resembles multithreaded programming because it requires
restricting visibility of memory changes – either through locking or transactions – to
other threads or instances of the program. But that is not the only similarity. The
other similarity, which we discuss in this section, is how sometimes low-level code
Figure 16-3. Example of using a PMEMoid in a persistent memory pool
Chapter 16 pMDK Internals: IMportant algorIthMs anD Data struCtures
