A set of progressive, non-intrusive, easy-to-use, and high performance persistent memory tools.
- Epoch Manager1
- Garbage List
- Persistent CAS 2
- PM allocator (in progress)
- Pool Management (in progress)
-
Progressive: components are self-sufficient, you don't need to include the whole forest just for a leaf.
-
Non-intrusive: pointers are 8-byte, typings are C++ standard.
-
Header-only
-
Persistent memory support, tested on real device.
-
High performance
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE={Debug|Release} -DPMEM={0|1} ..
make tests
1: Code adapted from PMwCAS with a few new features, all bugs are mine.
struct MockItem {
public:
MockItem() {}
static void Destroy(void* destroyContext, void* p) {
// some complex memory cleanup
}
/*
* Usage one: manual epoch management
* */
void SolveNP(){
epoch_manager_->Protect();
// some algorithms to solve SAT
epoch_manager_->Unprotect();
}
/*
* Usage two: use epoch guard to automatically protect and unprotect
* Makes it easy to ensure epoch protection boundaries tightly adhere to stack life
* time even with complex control flow.
* */
void SolveP(){
EpochGuard guard();
// Some algorithms
}
};
EpochManager epoch_manager_;
epoch_manager_.Initialize()
GarbageList garbage_list_;
// 1024 is the number of addresses that can be held aside for pointer stability.
garbage_list_.Initialize(&epoch_manager_, 1024);
// MockItem::Destory is the deallocation callback
garbage_list_.Push(new MockItem(), MockItem::Destroy, nullptr);
garbage_list_.Push(new MockItem(), MockItem::Destroy, nullptr);
We require PMDK to support safe and efficient persistent memory operations.
pool_ = pmemobj_open(pool_name, layout_name, pool_size, CREATE_MODE_RW);
// to create a new garbage list
garbage_list_.Initialize(&epoch_manager_, pool_, 1024);
// to recover an existing garbage list
garbage_list_.Recovery(&epoch_manager_, pool_);
Some persistent memory allocator, e.g. PMDK's, requires applications to pass a pre-existing memory location to store the pointer to the allocated memory.
For example:
void* mem = nullptr;
posix_memalign(&mem, CACHELINE_SIZE, size);
Storing mem
on the stack is not safe, thus requires the application to mantain an allocation list
,
or even change the implementation significantly, so there is this function:
Garbagelist::Item* mem = garbage_list_.ReserveItem();
posix_memalign(&mem->removed_item, CACHELINE_SIZE, size);
The mem->removed_item
is used to temporarily take the ownership of the allocated memory.
After the mem->removed_item
is handed back to the data structure, the reserved memory slot should be cleared, otherwise it will be reclaimed on recovery:
garbage_list_.ResetItem(mem);
Although both ReserveItem
and ResetItem
is crash/thread safe, when being used, typically they are protected by a (PMDK) transaction,
because these functions implicitly implied ownership transfer which requires multi-cache line operations.