NVMLib is library to optimally use a Hybrid RAM setup. It is designed to resemble malloc in its features and interfaces. It is designed to have little to no input from
the programmer regard the decision of where a perticular object needs to be placed.
-
Crash consistency:
We define a system crash consistent if all the objects in NVRAM which have not been
freedhave their state (i.e the value) persistent across runs. For instance:LIB_TOID(int) array = LIB_TOID(int)memalloc(10 * sizeof(int), NVRAM_HEAP); // allocated an array of size 10. // do something .... memfree(array); // freed the allocated space
if the program crashes before
memfreeis called, then the values inarrayhave to be in the same state as the run which crashed even in the next run. But oncememfreeis called, the next run starts with the initial state (in this case with all values being0).Note that in the above example
memallocis given the optional argument ofNVRAM_HEAPwhich tells the library to place the object in NVRAM. The current implementation of crash consistency is basic, it is capable of:- Recognising variables uniquely.
- Provide persistence to objects in
NVRAMacross runs. - Requires user to define what varible needs to go into
NVRAMfor their program to be crash consistent.
As the next step in providing crash consistency, we will be implementing context aware movement of objects from
DRAMtoNVRAM, i.e the library will move any object that it deems necessary for crash consistency intoNVRAMif it were inDRAM. -
Movement of objects from DRAM to NVRAM and vice versa:
The library is capable of moving objects from
NVRAMtoDRAMan vice-versa, inorder to improve the performance and/or the energy consumption.At present the library is capable of moving objects but the algorithm to move the objects taking performance into account is not yet implemented. What we mean by movement of objects is: Consider the following example:
LIB_TOID(int) array = LIB_TOID(int)memalloc(10 * sizeof(int)); // allocated an array of size 10. // initialisation for(int i = 0; i < 10; i++) { LIB_D_RW(array)[i] = rand() % 100; } printf("The value at idx 4 = %d\n", LIB_D_RO(array)[3]); // do something .... memfree(array); // freed the allocated space
Here note that the
memallochas been given only the size argument, thus the library allocates the memory for the object in the RAM that it deems most suitable (it considers thesizefor making the decision). TheLIB_D_ROandLIB_D_RWare the interfaces to read and write the object. These MACROS ensure that the data is always written and always the correct data is read. The reason why need these MACROs for access is because the object is moved from one RAM to another at runtime if the library thinks its good for the performance.For instance, in the above example, assume that
arraywas initially inDRAM. When thearrayis being initialised, it is doing a streaming write. Assume the library then decides that this variable is better if present inNVRAMand thus moves it (byfreeingtheDRAMlocation and allocating a new one inNVRAM). Now atprintfthe accessed location is no longer theDRAMone but it is theNVRAMone. This is the purpose thatLIB_D_ROandLIB_D_RWserve.
Internally all the allocations are represented as a <MEMoidKey, MEMoid> pair. The MEMoidKEey is returned to the user program, which is used for access and the MEMoid contains the information about the memory location and the access pointer to the location can be extracted from it. <MEMoidKey, MEMoid> is maintained in a persistent hashtable and this is how we are able to provide crash consistency.
MEMoidKey is in essense a pointer at the library level. This key is unique to every single variable in the user code and that is how we can ensure an un-freed variable is not reallocated in the next run.
Everytime LIB_D_RO or LIB_D_RW (these take the LIB_TOID object as argument. LIB_TOID object contains the MEMoidKey) is used, they internal call an internal function which takes the MEMoidKey, obtains the MEMoid object from the hashtable and returns the translated memory access pointer. Since the accesses have to happen this way and since we ensure that the movement of objects are reflected in the <MEMoidKey, MEMoid> hashtable, we ensure the validity of access pointer (note that the pointers are generated just-in-time for the access). We also add certain thread synchronisation during accesses, using our gcc compiler Plugin (mem_track.cc) which solidifies validity of access pointer.
- Crash consistency out of the box
- Ability to move the object from NVRAM to DRAM and vice-versa at runtime
- Provides the exact same interface as malloc and is just as feature-rich
- Provides the user the ability to define where a particular object needs to go (DRAM or NVRAM) if he chooses to (it's not necessary)
- The library overhead is just 40MB irrespective of the size of the program/its allocations
- The performance is comparable to
mallocfrom the 2nd run - The library demands no intervention or hints from the programmer. Any hints provided are considered with priority though.
- We use hashtables (with Fibonacci and cuckoo hashing) and splay trees as internal data-structures to make the queries
O(1) - We offload all the object maintenance tasks like moving objects, deletion, other maintenance-related calculations onto either the
logisticsthread or thedeletionthread and hence do not add latency to the user program (running on the main thread). At the same time we ensure data integrity and also the returned "access-pointer's" validity.
- Clone the repo
$ git clone https://github.com/gautamramk/NVMLib- Make a new build directory
$ cd NVMLib
$ mkdir buildcdinto the build directory and executemake
$ cd build
$ export CC=<path to C compiler>
$ export CXX=<path to C++ compiler>
$ cmake ..
$ makeHere export CC=<path to C compiler> export CXX=<path to C++ compiler> are optional, but recomended because cmake defaults to cc as the compiler.
Here we do cmake before make inorder to rebuild the Makefiles. This should be used when a additional files are added to the source code.
- For verbose build use
make VERBOSE=1
After make the shared library file (libNVMlib.so) and the gcc-plugin (libmem_track_plugin.so) will be in build/ and the intermediate files (.i) will be in build/intermediate/.
The documentation for the source code has been generated using Doxygen. The documentation can be found at NVMLib Documentation.
