├── Makefile ├── MyCheckTree.sh ├── README.md ├── common ├── .DS_Store ├── debug │ ├── dbg-nvmlog-log.cc │ └── dbg-nvmlog-undoredo.cc ├── keyinput.h ├── mempool.cc ├── mempool.h ├── nodepref.h ├── nvm-common.cc ├── nvm-common.h ├── performance.h ├── tree.cc └── tree.h ├── keygen-8B ├── Makefile ├── README ├── get20-80Insert.c ├── getdelete.c ├── getinsert.c ├── getscan.c ├── getstable.c ├── keygen.c ├── keygen.h ├── mycleankeys.sh ├── mygen.sh ├── showkey.c └── statkey.c └── lbtree-src ├── lbtree.cc └── lbtree.h /Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | 3 | # Flag for debugging runs 4 | # CFLAGS=-O0 -g -std=c++11 -pthread -mrtm -msse4.1 -mavx2 5 | 6 | # Flag for test runs 7 | CFLAGS=-O3 -std=c++11 -pthread -mrtm -msse4.1 -mavx2 8 | 9 | INCLUDE=-I./common 10 | LIB=-lpmem 11 | 12 | COMMON_DEPENDS= ./common/tree.h ./common/tree.cc ./common/keyinput.h ./common/mempool.h ./common/mempool.cc ./common/nodepref.h ./common/nvm-common.h ./common/nvm-common.cc ./common/performance.h 13 | COMMON_SOURCES= ./common/tree.cc ./common/mempool.cc ./common/nvm-common.cc 14 | 15 | # ----------------------------------------------------------------------------- 16 | TARGETS=lbtree 17 | 18 | #wbtree fptree 19 | 20 | all: ${TARGETS} 21 | 22 | # ----------------------------------------------------------------------------- 23 | 24 | lbtree: lbtree-src/lbtree.h lbtree-src/lbtree.cc ${COMMON_DEPENDS} 25 | ${CC} -o $@ ${CFLAGS} ${INCLUDE} lbtree-src/lbtree.cc ${COMMON_SOURCES} ${LIB} 26 | 27 | fptree: fptree-src/fptree.h fptree-src/fptree.cc ${COMMON_DEPENDS} 28 | ${CC} -o $@ ${CFLAGS} ${INCLUDE} fptree-src/fptree.cc ${COMMON_SOURCES} ${LIB} 29 | 30 | wbtree: wbtree-src/wbtree.h wbtree-src/wbtree.cc ${COMMON_DEPENDS} 31 | ${CC} -o $@ ${CFLAGS} ${INCLUDE} wbtree-src/wbtree.cc ${COMMON_SOURCES} ${LIB} 32 | 33 | # ----------------------------------------------------------------------------- 34 | clean: 35 | -rm -rf a.out core *.s ${TARGETS} 36 | -------------------------------------------------------------------------------- /MyCheckTree.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# != 1 ]; then 4 | echo "Usage: $0 "; 5 | exit 0; 6 | fi 7 | 8 | if [ ! -x $1 ]; then 9 | echo "$1 is not an executable"; 10 | exit 0; 11 | fi 12 | 13 | nvmfile=/mnt/mypmem0/chensm/leafdata 14 | 15 | cmdinit="$1 thread 2 mempool 50 nvmpool ${nvmfile} 200" 16 | 17 | echo 'debug_bulkload' 18 | echo -n 'Test 1: ' 19 | ${cmdinit} debug_bulkload 123 1.0 | grep good 20 | echo -n 'Test 2: ' 21 | ${cmdinit} debug_bulkload 500 0.9 | grep good 22 | echo -n 'Test 3: ' 23 | ${cmdinit} debug_bulkload 1234567 1.0 | grep good 24 | echo -n 'Test 4: ' 25 | ${cmdinit} debug_bulkload 1234567 0.7 | grep good 26 | 27 | echo 'debug_insert' 28 | echo -n 'Test 5: ' 29 | ${cmdinit} debug_insert 10 | grep good 30 | echo -n 'Test 6: ' 31 | ${cmdinit} debug_insert 102 | grep good 32 | echo -n 'Test 7: ' 33 | ${cmdinit} debug_insert 1025 | grep good 34 | echo -n 'Test 8: ' 35 | ${cmdinit} debug_insert 31025 | grep good 36 | echo -n 'Test 9: ' 37 | ${cmdinit} debug_insert 371025 | grep good 38 | 39 | echo 'debug_del' 40 | echo -n 'Test 10: ' 41 | ${cmdinit} debug_del 10 | grep good 42 | echo -n 'Test 11: ' 43 | ${cmdinit} debug_del 120 | grep good 44 | echo -n 'Test 12: ' 45 | ${cmdinit} debug_del 3120 | grep good 46 | echo -n 'Test 13: ' 47 | ${cmdinit} debug_del 83120 | grep good 48 | echo -n 'Test 14: ' 49 | ${cmdinit} debug_del 583120 | grep good 50 | 51 | echo 'debug_lookup' 52 | echo -n 'Test 15: ' 53 | ${cmdinit} debug_lookup 20 1.0 | grep good 54 | echo -n 'Test 16: ' 55 | ${cmdinit} debug_lookup 320 1.0 | grep good 56 | echo -n 'Test 17: ' 57 | ${cmdinit} debug_lookup 5320 1.0 | grep good 58 | echo -n 'Test 18: ' 59 | ${cmdinit} debug_lookup 101180 1.0 | grep good 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | This directory contains code for the following paper: 4 | 5 | **Jihang Liu, Shimin Chen**. *LB+-Trees: Optimizing Persistent Index Performance on 3DXPoint Memory*. PVLDB 13(7): 1078-1090. 6 | 7 | 8 | ## Machine Configuration 9 | 10 | The code is designed for Machines equipped with Intel Optane DC Persistent Memory. It uses PMDK to map NVM into the virtual address space of the process. It also uses Intel RTM for concurrency control purpose. 11 | 12 | Suppose the NVDIMM device is /dev/pmem0, a file system is created on the device, and it is mounted to /mnt/mypmem0. Make sure we have permissions to create and read files in /mnt/mypmem0. For example, we can create a subdirectory for each user that wants to work on NVM, then use chown to change the owner of the subdirectory to the user. 13 | 14 | Next, create a file on NVM for storing the leaf nodes of the B+-trees: 15 | 16 | $ dd if=/dev/zero of=/mnt/mypmem0/user/filename bs=1048576 count=num-MB 17 | 18 | ## Directory Structure 19 | 20 | The directory is organized as follows: 21 | 22 | Name | Explanation 23 | ------------ | ------------- 24 | Makefile | used to compile the code for the tree implementation 25 | MyCheckTree.sh | simple correctness checks 26 | common | common headers and main driver program 27 | lbtree-src | LB+-Tree 28 | fptree-src | FP-Tree 29 | wbtree-src | WB+-Tree 30 | keygen-8B | generate 8B keys for experiments 31 | 32 | ## Compilation 33 | 34 | ``` 35 | $ make 36 | ``` 37 | 38 | ## Command Line Options 39 | 40 | ``` 41 | Usage: ./lbtree [ ] ... 42 | -------------------------------------------------- 43 | [Initialization] 44 | thread must be the first command, followed by mempool and nvmpool. 45 | 46 | thread 47 | mempool 48 | nvmpool 49 | -------------------------------------------------- 50 | [Debugging] 51 | use these commands to test the correctness of the implementation 52 | 53 | debug_bulkload 54 | debug_randomize 55 | debug_lookup 56 | debug_insert 57 | debug_del 58 | -------------------------------------------------- 59 | [Test Preparation] 60 | prepare a tree before performance tests 61 | 62 | bulkload 63 | randomize 64 | stable 65 | -------------------------------------------------- 66 | [Performance Tests] 67 | measure performance of various tree operations 68 | 69 | lookup 70 | insert 71 | del 72 | -------------------------------------------------- 73 | [Misc] 74 | helper commands. debug_test enables correctness check for performance tests. 75 | 76 | print_tree 77 | check_tree 78 | print_mem 79 | debug_test 80 | sleep 81 | -------------------------------------------------- 82 | ``` 83 | 84 | ## Simple Correctness Check 85 | 86 | MyCheckTree.sh contains a number of example debugging tests. 87 | 88 | ``` 89 | $ ./MyCheckTree.sh ./lbtree 90 | debug_bulkload 91 | Test 1: bulkload is good! 92 | Test 2: bulkload is good! 93 | Test 3: bulkload is good! 94 | Test 4: bulkload is good! 95 | debug_insert 96 | Test 5: insertion is good! 97 | Test 6: insertion is good! 98 | Test 7: insertion is good! 99 | Test 8: insertion is good! 100 | Test 9: insertion is good! 101 | debug_del 102 | Test 10: delete is good! 103 | Test 11: delete is good! 104 | Test 12: delete is good! 105 | Test 13: delete is good! 106 | Test 14: delete is good! 107 | debug_lookup 108 | Test 15: lookup is good! 109 | Test 16: lookup is good! 110 | Test 17: lookup is good! 111 | Test 18: lookup is good! 112 | ``` 113 | 114 | ## Generate Keys for Experiments 115 | 116 | ``` 117 | $ cd keygen-8B/ 118 | $ make 119 | $ ./mygen.sh 120 | ``` 121 | 122 | mygen.sh generates 50K bulkload keys, 500 random search keys, 500 insert keys, and 500 delete keys. The tools guarantee that the insert keys are not in bulkload keys and the delete keys are all in bulkload keys. In this way, all insertions and deletions will succeed. 123 | 124 | mygen.sh can be modified to generate the desired test keys. 125 | 126 | ## Test Runs 127 | 1. Set up optional environment variables 128 | 129 | PMEM_MMAP_HINT can control the NVM mapping address in PMDK. For simplicity, we also set NVMFILE to be the NVM file path. 130 | ``` 131 | $ export NVMFILE="/mnt/mypmem0/chensm/leafdata" 132 | $ export PMEM_MMAP_HINT="0x600000000000" 133 | ``` 134 | 2. Lookup test 135 | 136 | We bulkload 50K keys to make the tree 100% full. Then we performs 500 random lookups. The number of worker threads is 2. Both bulkload and lookup use 2 threads. Leaf nodes are in the NVM pool, which is 200MB in total (and 200MB/2 per thread). Similarly, non-leaf nodes are in the DRAM pool, which is 100MB in total (and 100MB/2 per thread). 137 | ``` 138 | $ ./lbtree thread 2 mempool 100 nvmpool ${NVMFILE} 200 bulkload 50000 keygen-8B/dbg-k50k 1.0 lookup 500 keygen-8B/dbg-lookup500 139 | ``` 140 | 141 | 3. Insert test 142 | 143 | We bulkload 50K keys to make the tree 100% full. Then we performs 500 random insertions. 144 | ``` 145 | $ ./lbtree thread 2 mempool 100 nvmpool ${NVMFILE} 200 bulkload 50000 keygen-8B/dbg-k50k 1.0 insert 500 keygen-8B/dbg-insert500 146 | ``` 147 | 148 | 4. Delete test 149 | 150 | We bulkload 50K keys to make the tree 100% full. Then we performs 500 random deletions. 151 | ``` 152 | $ ./lbtree thread 2 mempool 100 nvmpool ${NVMFILE} 200 bulkload 50000 keygen-8B/dbg-k50k 1.0 del 500 keygen-8B/dbg-del500 153 | ``` -------------------------------------------------------------------------------- /common/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schencoding/lbtree/92f7304feef4c62a90fe0b2b6e29bd427649d38e/common/.DS_Store -------------------------------------------------------------------------------- /common/debug/dbg-nvmlog-log.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * @file dbg-nvmlog-log.cc 3 | * @author Shimin Chen 4 | * @version 1.0 5 | * 6 | * @section LICENSE 7 | * 8 | * TBD 9 | * 10 | * @section DESCRIPTION 11 | * 12 | * check the correctness of class NvmLog_Log 13 | * 14 | * compilation: 15 | * g++ -O3 -std=c++11 -pthread -I../ ../mempool.cc dbg-nvmlog-log.cc -lpmem -o dbg-nvmloglog 16 | */ 17 | 18 | #define NVM_LOG_SIZE 256 // 4 cache lines 19 | 20 | #include "nvm-common.h" 21 | 22 | NvmLog_Log mylogbuf; 23 | NvmLog_Log::nl_log_pointer_t readpos; 24 | 25 | char buf[1024]; 26 | int len; 27 | 28 | int main() 29 | { 30 | the_thread_nvmpools.init(3, "/mnt/mypmem0/chensm/leafdata", MB); 31 | worker_id= 0; 32 | 33 | mylogbuf.initLog(); 34 | mylogbuf.prepareLogforWriting(); 35 | mylogbuf.getLogCurPos(&readpos); 36 | 37 | while(1) { 38 | mylogbuf.printLog(); 39 | mylogbuf.printLogWritePos(); 40 | mylogbuf.printLogReadPos(&readpos); 41 | 42 | int cmd, retval; 43 | printf("\n0.exit 1.prepareLogforWriting 2.writeLog 3.flushLog\n" 44 | "4.getLogCurPos 5.prepareForRead 6.readLog 7.readLogSkip\n" 45 | "8.prepareForReverseRead 9.readLogReverse 10.readLogReverseSkip\n"); 46 | if(scanf("%d", &cmd)!=1) continue; 47 | 48 | if (cmd ==0) break; 49 | 50 | switch(cmd) { 51 | case 1: 52 | mylogbuf.prepareLogforWriting(); 53 | break; 54 | case 2: 55 | do { 56 | printf("record size: "); 57 | } while (scanf("%d", &len) != 1); 58 | for(int ii=0; ii 4 | * @version 1.0 5 | * 6 | * @section LICENSE 7 | * 8 | * TBD 9 | * 10 | * @section DESCRIPTION 11 | * 12 | * check the correctness of class NvmLog 13 | * 14 | * compilation: 15 | * g++ -O3 -std=c++11 -pthread -I../ ../mempool.cc dbg-nvmlog-undoredo.cc -lpmem -o dbg-nvmlog 16 | */ 17 | 18 | #define NVM_LOG_SIZE 1024 19 | 20 | #include "nvm-common.h" 21 | 22 | NvmLog mylog; 23 | 24 | unsigned long long i8B=8, j8B=0x18, k8B=0x48; 25 | unsigned int i4B=4, j4B=0x14, k4B=0x44; 26 | unsigned short i2B=2, j2B=0x12, k2B=0x42; 27 | unsigned char i1B=1, j1B=0x11, k1B=0x41; 28 | char ibuf[128]= "good morning"; 29 | char jbuf[128]= "good afternoon"; 30 | char kbuf[128]= "good evening"; 31 | 32 | void *node[8]; 33 | 34 | int main() 35 | { 36 | the_thread_nvmpools.init(3, "/mnt/mypmem0/chensm/leafdata", MB); 37 | worker_id= 0; 38 | 39 | mylog.init(); 40 | 41 | printf("begin\n"); 42 | mylog.startMiniTransaction(); 43 | 44 | printf ("mylog.write8B(&i8B, 0x28)\n"); 45 | mylog.write8B(&i8B, 0x28); 46 | node[0]= mylog.allocNode(64); 47 | mylog.delNode(node[0]); 48 | 49 | printf ("mylog.write4B(&i4B, 0x24)\n"); 50 | mylog.write4B(&i4B, 0x24); 51 | node[1]= mylog.allocNode(64); 52 | mylog.delNode(node[1]); 53 | 54 | printf ("mylog.write2B(&i2B, 0x22)\n"); 55 | mylog.write2B(&i2B, 0x22); 56 | node[2]= mylog.allocNode(64); 57 | mylog.delNode(node[2]); 58 | 59 | printf ("mylog.write1B(&i1B, 0x21)\n"); 60 | mylog.write1B(&i1B, 0x21); 61 | node[3]= mylog.allocNode(64); 62 | mylog.delNode(node[3]); 63 | 64 | printf ("mylog.writeVchar(ibuf, 12, GOOD MORNING)\n"); 65 | mylog.writeVchar(ibuf, 12, "GOOD MORNING"); 66 | node[4]= mylog.allocNode(64); 67 | mylog.delNode(node[4]); 68 | 69 | printf ("mylog.new8B(&j8B, 0x38)\n"); 70 | mylog.new8B(&j8B, 0x38); 71 | node[5]= mylog.allocNode(64); 72 | mylog.delNode(node[5]); 73 | 74 | printf ("mylog.new4B(&j4B, 0x34)\n"); 75 | mylog.new4B(&j4B, 0x34); 76 | node[6]= mylog.allocNode(64); 77 | mylog.delNode(node[6]); 78 | 79 | printf ("mylog.new2B(&j2B, 0x32)\n"); 80 | mylog.new2B(&j2B, 0x32); 81 | node[7]= mylog.allocNode(64); 82 | mylog.delNode(node[7]); 83 | 84 | printf ("mylog.new1B(&j1B, 0x31)\n"); 85 | mylog.new1B(&j1B, 0x31); 86 | 87 | printf ("mylog.newVchar(jbuf, 14, GOOD AFTERNOON)\n"); 88 | mylog.newVchar(jbuf, 14, "GOOD AFTERNOON"); 89 | 90 | printf ("mylog.redoWrite8B(&k8B, 0x58)\n"); 91 | mylog.redoWrite8B(&k8B, 0x58); 92 | 93 | printf ("mylog.redoWrite4B(&k4B, 0x54)\n"); 94 | mylog.redoWrite4B(&k4B, 0x54); 95 | 96 | printf ("mylog.redoWrite2B(&k2B, 0x52)\n"); 97 | mylog.redoWrite2B(&k2B, 0x52); 98 | 99 | printf ("mylog.redoWrite1B(&k1B, 0x51)\n"); 100 | mylog.redoWrite1B(&k1B, 0x51); 101 | 102 | printf ("mylog.redoWriteVchar(kbuf, 12, GOOD EVENING)\n"); 103 | mylog.redoWriteVchar(kbuf, 12, "GOOD EVENING"); 104 | #if 0 105 | #endif 106 | 107 | printf ("(1) commit (0) abort :"); 108 | int choice; 109 | while (scanf("%d", &choice) != 1); 110 | 111 | if (choice == 1) { 112 | printf ("mylog.commitMiniTransaction()\n"); 113 | mylog.commitMiniTransaction(); 114 | } 115 | else { 116 | printf ("mylog.abortMiniTransaction()\n"); 117 | mylog.abortMiniTransaction(); 118 | } 119 | 120 | // mylog.nl_logbuf_.printLog(); 121 | mylog.print(); 122 | 123 | 124 | printf("i8B=%llx, j8B=%llx, k8B=%llx\n", i8B, j8B, k8B); 125 | printf("i4B=%x, j4B=%x, k4B=%x\n", i4B, j4B, k4B); 126 | printf("i2B=%x, j2B=%x, k2B=%x\n", i2B, j2B, k2B); 127 | printf("i1B=%x, j1B=%x, k1B=%x\n", i1B, j1B, k1B); 128 | printf("ibuf=%s, jbuf=%s, kbuf=%s\n", ibuf, jbuf, kbuf); 129 | 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /common/keyinput.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file keyinput.h 3 | * @author Shimin Chen , Jihang Liu, Leying Chen 4 | * @version 1.0 5 | * 6 | * @section LICENSE 7 | * 8 | * TBD 9 | * 10 | * @section DESCRIPTION 11 | * 12 | * keyInput provides the mechanisms to read index keys from files. 13 | */ 14 | 15 | #ifndef _BTREE_KEYINPUT_H 16 | #define _BTREE_KEYINPUT_H 17 | /* ---------------------------------------------------------------------- */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | /* ---------------------------------------------------------------------- */ 29 | 30 | typedef long long Int64; 31 | 32 | /** 33 | * keyInput class defines the get_key virtual method. 34 | */ 35 | class keyInput { 36 | public: 37 | /** 38 | * get the index-th key 39 | * 40 | * @param index the index-th key 41 | * @return the key 42 | */ 43 | virtual Int64 get_key (Int64 index) 44 | { 45 | fprintf (stderr, "get_key is not implemented!\n"); 46 | exit (1); 47 | } 48 | 49 | /** 50 | * each thread can open cursor to read from a different location 51 | * if the keys are already in an array, then do nothing 52 | */ 53 | virtual keyInput *openCursor(Int64 start_key, Int64 keynum) {return this;} 54 | 55 | virtual void closeCursor(keyInput * cursor) { } 56 | 57 | }; // keyInput 58 | 59 | 60 | /** 61 | * bufferedKeyInput class gradually reads keys from a file using a memory buffer 62 | */ 63 | class bufferedKeyInput: public keyInput { 64 | private: 65 | const char *key_file; 66 | int key_fd; 67 | Int64 key_num; // total number of keys 68 | Int64 key_bottom; // absolute index of key_buffer[0] 69 | Int64 key_top; // absolute index beyond keys in buffer 70 | Int64 key_start; 71 | 72 | #define KEY_BUFFER_SIZE (1024*1024) 73 | Int64 *key_buffer; 74 | 75 | public: 76 | /** 77 | * constructor 78 | * 79 | * @param filename the file to read keys from 80 | * @param start_key read from start_key from the file 81 | * @param keynum the number of keys to read 82 | */ 83 | bufferedKeyInput (const char *filename, Int64 start_key, Int64 keynum) 84 | { 85 | key_file= filename; 86 | key_fd = open (filename, 0, 0600); 87 | if (key_fd < 0) { 88 | perror (filename); exit (1); 89 | } 90 | 91 | Int64 myoff= start_key*sizeof(Int64); 92 | if (lseek(key_fd, myoff, SEEK_SET) != myoff) { 93 | fprintf(stderr, "can't seek to start_key=%lld off=%lld\n", 94 | start_key, myoff); 95 | perror("lseek"); exit(1); 96 | } 97 | 98 | key_num = keynum; 99 | key_bottom = 0; 100 | key_top = 0; 101 | key_start= start_key; 102 | 103 | key_buffer= (Int64 *) memalign(4096, KEY_BUFFER_SIZE); 104 | if (!key_buffer) {perror("memalign"); exit(1);} 105 | } 106 | 107 | ~bufferedKeyInput () 108 | { 109 | free (key_buffer); 110 | close (key_fd); 111 | } 112 | 113 | // index should be non-descending 114 | // index starts at start_key 115 | Int64 get_key (Int64 index) 116 | {int len; 117 | 118 | index -= key_start; 119 | 120 | while (index >= key_top) { 121 | len = read (key_fd, key_buffer, KEY_BUFFER_SIZE); 122 | if (len < 0) { perror("read"); exit(1); } 123 | key_bottom = key_top; 124 | key_top += len/sizeof(Int64); 125 | } 126 | 127 | Int64 pos = index - key_bottom; 128 | assert (pos >= 0); 129 | return key_buffer[pos]; 130 | } 131 | 132 | keyInput *openCursor(Int64 start_key, Int64 keynum) 133 | { 134 | return new bufferedKeyInput(key_file, start_key, keynum); 135 | } 136 | 137 | void closeCursor(keyInput * cursor) 138 | { 139 | bufferedKeyInput *p= (bufferedKeyInput *)cursor; 140 | delete p; 141 | } 142 | 143 | }; // bufferedKeyInput 144 | 145 | /** 146 | * simpleKeyInput class generates natural numbers as keys 147 | */ 148 | class simpleKeyInput: public keyInput { 149 | private: 150 | Int64 key_num; 151 | Int64 key_start; 152 | Int64 key_step; 153 | 154 | public: 155 | /** 156 | * constructor 157 | * 158 | * @param num number of keys to generate 159 | * @param start the start index of the keys 160 | * @param step number of keys to skip per get_key 161 | */ 162 | simpleKeyInput (Int64 num, Int64 start, Int64 step) 163 | { 164 | key_num = num; 165 | key_start = start; 166 | key_step = step; 167 | } 168 | 169 | Int64 get_key (Int64 index) 170 | { 171 | return (key_start + key_step * index); 172 | } 173 | }; 174 | 175 | /** 176 | * inMemKeyInput class generates random keys 177 | */ 178 | class inMemKeyInput: public keyInput { 179 | public: 180 | Int64 key_num; 181 | Int64 *keys; 182 | Int64 key_start; 183 | Int64 key_step; 184 | 185 | private: 186 | static inline Int64 gen_a_key() 187 | { 188 | Int64 a= random(); 189 | Int64 b= random(); 190 | Int64 c= random(); 191 | return (((a<<32)^(b<<16)^(c)) & (0x7FFFFFFFFFFFFFFFLL)); 192 | } 193 | 194 | #define KEYS_FOR_DBG 195 | 196 | static void keygen (Int64 keynum, Int64 key[]) 197 | {Int64 i; 198 | srandom (time(NULL)); 199 | for (i=0; i0)?1: ((tt<0)?-1:0)); 216 | } 217 | 218 | static void sortkey (Int64 keynum, Int64 key[]) 219 | {Int64 i, count; 220 | do { 221 | qsort (key, keynum, sizeof(Int64), inMemKeyInput::compare); 222 | count = 0; 223 | for (i=0; i 0) 229 | printf ("%lld duplicates found\n", count); 230 | } while (count > 0); 231 | } 232 | 233 | 234 | public: 235 | /** 236 | * generate sorted random keys 237 | * 238 | * @param num number of keys to generate 239 | * @param start the start index of the keys 240 | * @param step number of keys to skip per get_key 241 | */ 242 | inMemKeyInput (Int64 num, Int64 start, Int64 step) 243 | { 244 | key_num= num; 245 | keys= new Int64[num]; 246 | assert(keys); 247 | 248 | keygen(key_num, keys); 249 | sortkey(key_num, keys); 250 | 251 | key_start= start; 252 | key_step= step; 253 | } 254 | 255 | ~inMemKeyInput() 256 | { 257 | delete[] keys; 258 | } 259 | 260 | Int64 get_key (Int64 index) 261 | { 262 | Int64 ii= key_start + key_step*index; 263 | assert((0<=ii) && (ii, Jihang Liu, Leying Chen 4 | * @version 1.0 5 | * 6 | * @section LICENSE 7 | * 8 | * TBD 9 | * 10 | * @section DESCRIPTION 11 | * 12 | * The mempool class implements a memory pool for experiments purpose. It will 13 | * allocate a contiguous region of DRAM from OS. Then it will serve memory 14 | * allocation requests from this memory pool. Freed btree nodes will be 15 | * appended into a linked list for future reuse. 16 | * 17 | * The memory pool is divided into segments. Each worker thread has its own 18 | * segment of the memory pool to reduce contention. 19 | */ 20 | 21 | #include "mempool.h" 22 | 23 | thread_local int worker_id= -1; /* in Thread Local Storage */ 24 | 25 | threadMemPools the_thread_mempools; 26 | threadNVMPools the_thread_nvmpools; 27 | 28 | /* -------------------------------------------------------------- */ 29 | void threadMemPools::init (int num_workers, long long size, long long align) 30 | { 31 | assert((num_workers>0)&&(size>0)&&(align>0)&((align&(align-1))==0)); 32 | 33 | // 1. allocate memory 34 | tm_num_workers= num_workers; 35 | tm_pools= new mempool[tm_num_workers]; 36 | 37 | long long size_per_pool= (size/tm_num_workers/align)*align; 38 | size_per_pool= (size_per_pool0)&&(size>0)&&(size % 4096 == 0)); 116 | 117 | // set sigbus handler 118 | signal(SIGBUS, handleSigbus); 119 | 120 | // 1. allocate memory 121 | tm_num_workers= num_workers; 122 | tm_pools= new mempool[tm_num_workers]; 123 | if (!tm_pools) { perror ("malloc"); exit (1); } 124 | 125 | tn_nvm_file= nvm_file; 126 | 127 | long long size_per_pool= (size/tm_num_workers/4096)*4096; 128 | size_per_pool= (size_per_pool 252 | 253 | void * test2ThMain(void *arg) 254 | { 255 | worker_id= (int) (long long) arg; 256 | 257 | // 1. alloc and free 258 | char *p= (char *)mempool_alloc(worker_id*64); 259 | printf("alloc(%d)= %p\n", worker_id*64, p); 260 | 261 | // 3. alloc_node, free_node 262 | char *pnode[5]; 263 | for (int i=0; i<5; i++) { 264 | pnode[i]= (char *)mempool_alloc_node(64); 265 | printf("worker %d mempool_alloc_node(64)=%p\n", worker_id, pnode[i]); 266 | } 267 | printf("worker %d free all allocated nodes\n", worker_id); 268 | for (int i=0; i<5; i++) { 269 | mempool_free_node(pnode[i]); 270 | } 271 | 272 | // 4. alloc node again 273 | for (int i=0; i<3; i++) { 274 | pnode[i]= (char *)mempool_alloc_node(64); 275 | printf("worker %d mempool_alloc_node(64)=%p\n", worker_id, pnode[i]); 276 | } 277 | 278 | return NULL; 279 | } 280 | 281 | // 3 threads, each allocating and freeing nodes. 282 | void test2() 283 | { 284 | pthread_t tid[3]; 285 | 286 | the_thread_mempools.init(3, 2*1024*1024, 64); // 3 workers 287 | the_thread_mempools.print(); 288 | 289 | for (int i=0; i<3; i++) { 290 | if (pthread_create(&(tid[i]), NULL, test2ThMain, (void *)(long long)i) != 0) { 291 | fprintf(stderr, "pthread_create error!\n"); exit(1); 292 | } 293 | } 294 | for (int i=0; i<3; i++) { 295 | void *retval= NULL; 296 | pthread_join(tid[i], &retval); 297 | } 298 | the_thread_mempools.print(); 299 | } 300 | 301 | void * test3ThMain(void *arg) 302 | { 303 | worker_id= (int) (long long) arg; 304 | 305 | // 1. alloc and free 306 | char *p= (char *)nvmpool_alloc(worker_id*64); 307 | printf("alloc(%d)= %p\n", worker_id*64, p); 308 | 309 | // 3. alloc_node, free_node 310 | char *pnode[5]; 311 | for (int i=0; i<5; i++) { 312 | pnode[i]= (char *)nvmpool_alloc_node(64); 313 | printf("worker %d nvmpool_alloc_node(64)=%p\n", worker_id, pnode[i]); 314 | } 315 | printf("worker %d free all allocated nodes\n", worker_id); 316 | for (int i=0; i<5; i++) { 317 | nvmpool_free_node(pnode[i]); 318 | } 319 | 320 | // 4. alloc node again 321 | for (int i=0; i<3; i++) { 322 | pnode[i]= (char *)nvmpool_alloc_node(64); 323 | printf("worker %d nvmpool_alloc_node(64)=%p\n", worker_id, pnode[i]); 324 | } 325 | 326 | return NULL; 327 | } 328 | 329 | // nvm pool 330 | void test3() 331 | { 332 | pthread_t tid[3]; 333 | 334 | printf("test3\n"); 335 | 336 | //char line[80]; 337 | //sprintf(line, "cat /proc/%d/maps", getpid()); 338 | //if (system(line) < 0) { 339 | // perror("system"); 340 | //} 341 | 342 | the_thread_nvmpools.init(3, "/mnt/mypmem0/chensm/leafdata", MB); 343 | the_thread_nvmpools.print(); 344 | 345 | for (int i=0; i<3; i++) { 346 | if (pthread_create(&(tid[i]), NULL, test3ThMain, (void *)(long long)i) != 0) { 347 | fprintf(stderr, "pthread_create error!\n"); exit(1); 348 | } 349 | } 350 | for (int i=0; i<3; i++) { 351 | void *retval= NULL; 352 | pthread_join(tid[i], &retval); 353 | } 354 | the_thread_nvmpools.print(); 355 | } 356 | 357 | int main() 358 | { 359 | test3(); 360 | return 0; 361 | } 362 | 363 | /* compile 364 | 365 | $ g++ -O3 -std=c++11 -pthread mempool.cc -lpmem -o dbg-mempool 366 | 367 | */ 368 | 369 | #endif // 1 to enable, 0 to disable 370 | -------------------------------------------------------------------------------- /common/mempool.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mempool.h 3 | * @author Shimin Chen , Jihang Liu, Leying Chen 4 | * @version 1.0 5 | * 6 | * @section LICENSE 7 | * 8 | * TBD 9 | * 10 | * @section DESCRIPTION 11 | * 12 | * The mempool class implements a memory pool for experiments purpose. It will 13 | * manage a contiguous region of memory (DRAM or NVM). Then it will serve memory 14 | * allocation requests from this memory pool. Freed btree nodes will be 15 | * appended into a linked list for future reuse. 16 | * 17 | * threadMemPools allocates contiguous region of DRAM using memalign from OS. 18 | * The memory is divided into segments. A mempool instance is used to manage 19 | * a segment. Each worker thread has its own mempool instance. 20 | * A thread local variable worker_id is used to locate the current mempool 21 | * used by the calling thread. 22 | * 23 | * threadNVMPools allocates and maps contiguous NVM. Otherwise, it is similar 24 | * to threadMemPools. Each worker thread has its own mempool instance for 25 | * NVM allocation and free. 26 | * 27 | * During crash recovery, the B+-Tree object will take the first 4KB. 28 | * We can find the first nonleaf node. Then, the NVM can be scanned to 29 | * determine the allocated nodes and unused nodes. 30 | * 31 | */ 32 | 33 | #ifndef _BTREE_MEM_POOL_H 34 | #define _BTREE_MEM_POOL_H 35 | 36 | /* -------------------------------------------------------------- */ 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | /* -------------------------------------------------------------- */ 47 | /* NVMPOOL_REAL: use pmdk to map NVM 48 | * undefined: use memalign to allocate memory to simulate NVM 49 | */ 50 | #define NVMPOOL_REAL // comment this out for using DRAM as NVM 51 | 52 | #ifdef NVMPOOL_REAL 53 | // use 54 | // PMEM_MMAP_HINT=desired_address 55 | // to map to a desired address 56 | #include 57 | #endif 58 | 59 | /* -------------------------------------------------------------- */ 60 | 61 | #ifndef MB 62 | #define MB (1024*1024) 63 | #endif 64 | 65 | /** 66 | * mempool: allocate memory using malloc-like calls then manage the memory 67 | * by itself 68 | */ 69 | class mempool { 70 | private: 71 | long long mempool_align; 72 | long long mempool_size; 73 | char * mempool_start; 74 | char * mempool_cur; 75 | char * mempool_end; 76 | char * mempool_free_node; 77 | const char * mempool_name; 78 | 79 | public: 80 | // --- 81 | // Initialization and basics 82 | // --- 83 | 84 | /** 85 | * constructor to set all internal states to null 86 | */ 87 | mempool () 88 | {mempool_start = mempool_cur = mempool_end = NULL; 89 | mempool_free_node = NULL; 90 | } 91 | 92 | /** 93 | * destructor does nothing 94 | */ 95 | ~mempool () {} 96 | 97 | /** 98 | * initialize the memory pool. 99 | * 100 | * @param start the memory pool start address 101 | * @param size the memory pool size in bytes 102 | * @param align alignment in bytes 103 | */ 104 | void init (char *start, long long size, long long align, const char * name) 105 | { 106 | mempool_align = align; 107 | mempool_size = size; 108 | mempool_start = start; 109 | 110 | mempool_cur = mempool_start; 111 | mempool_end = mempool_start + size; 112 | mempool_free_node = NULL; 113 | 114 | mempool_name= name; 115 | } 116 | 117 | /** 118 | * obtain the starting address of the memory pool 119 | */ 120 | char * get_base () {return mempool_start;} 121 | 122 | /** 123 | * print all the parameters and addresses of the memory pool 124 | */ 125 | void print_params (FILE *fp=stdout) 126 | { 127 | fprintf (fp, "%s\n", mempool_name); 128 | fprintf (fp, "mempool_align=%lld\n", mempool_align); 129 | fprintf (fp, "mempool_size=%lld\n", mempool_size); 130 | fprintf (fp, "mempool_start=%p\n", mempool_start); 131 | fprintf (fp, "mempool_cur=%p\n", mempool_cur); 132 | fprintf (fp, "mempool_end=%p\n", mempool_end); 133 | fprintf (fp, "mempool_free_node=%p\n\n", mempool_free_node); 134 | } 135 | 136 | void print_usage () 137 | { 138 | long long used= (mempool_cur - mempool_start); 139 | long long ff= 0; 140 | for(char *p = mempool_free_node; p; p= *((char **)p)) 141 | ff ++; 142 | 143 | printf("%s: total %.1lfMB, use %.1lfMB, among which %lld free nodes\n", 144 | mempool_name, ((double)mempool_size)/MB, ((double)used)/MB, ff); 145 | } 146 | 147 | public: 148 | // --- 149 | // allocation and free 150 | // --- 151 | 152 | /** 153 | * allocate a piece of memory from the memory pool 154 | * 155 | * @param size the allocation size 156 | * @return the starting address of the allocated memory 157 | * 158 | * Note: the memory pool will be allocated from the end. 159 | * Make sure the size is aligned. The code will not check this. 160 | */ 161 | void * alloc (unsigned long long size) 162 | { 163 | if (mempool_cur + size <= mempool_end) { 164 | register char *p; 165 | p = mempool_cur; 166 | mempool_cur += size; 167 | return (void *) p; 168 | } 169 | fprintf (stderr, "%s alloc - run out of memory!\n", mempool_name); 170 | exit (1); 171 | } 172 | 173 | /** 174 | * free memory that is previously allocated using alloc() 175 | * 176 | * @param p the starting address of the memory 177 | */ 178 | void free (void *p) 179 | { } 180 | 181 | /** 182 | * allocate a btree node 183 | * 184 | * @param size the size of the node 185 | * @return the starting address of the allocated node 186 | * 187 | * size should be always the same, and it should be larger than 188 | * sizeof(void *) ||| won't check this 189 | */ 190 | void * alloc_node (int size) 191 | { 192 | if (mempool_free_node) { 193 | register char *p; 194 | p = mempool_free_node; 195 | mempool_free_node = *((char **)p); 196 | return (void *) p; 197 | } 198 | else { 199 | return alloc (size); 200 | } 201 | } 202 | 203 | /** 204 | * free a btree node and append it into a linked list for future alloc_node 205 | * 206 | * @param p btree node to free 207 | */ 208 | void free_node (void *p) 209 | { 210 | *((char **)p) = mempool_free_node; 211 | mempool_free_node = (char *)p; 212 | } 213 | 214 | /** 215 | * print the nodes on the free linked list 216 | */ 217 | void print_free_nodes () 218 | { 219 | char *p = mempool_free_node; 220 | 221 | printf ("%s free nodes:\n", mempool_name); 222 | while (p != NULL) { 223 | printf ("%p -> ", p); 224 | p = *((char **)p); 225 | } 226 | printf ("nil\n\n"); 227 | } 228 | 229 | }; // mempool 230 | 231 | /* -------------------------------------------------------------- */ 232 | 233 | /** 234 | * threadMemPools allocates a pool of memory from OS. It divides the 235 | * memory into num workers's sub pools. Then it uses mempool to manage 236 | * the sub pool. 237 | */ 238 | class threadMemPools { 239 | public: 240 | mempool *tm_pools; /* pools[0..num_workers-1] */ 241 | int tm_num_workers; 242 | 243 | char * tm_buf; /* start address of allocated memory */ 244 | long long tm_size; /* tm_buf size */ 245 | 246 | public: 247 | /** 248 | * Constructor sets all fields to empty 249 | */ 250 | threadMemPools() 251 | {tm_pools= NULL; tm_num_workers= 0; 252 | tm_buf= NULL; tm_size= 0; 253 | } 254 | 255 | /** 256 | * Destructor frees memory if exists. 257 | */ 258 | ~threadMemPools() 259 | {if(tm_buf) { 260 | free(tm_buf); 261 | tm_buf= NULL; 262 | } 263 | if(tm_pools) { 264 | delete[] tm_pools; 265 | tm_pools= NULL; 266 | } 267 | } 268 | 269 | 270 | /** 271 | * Initialize the memory pools 272 | * allocate memory from OS and initialize all per-worker mempool. 273 | * 274 | * @param num_workers number of parallel worker threads 275 | * @param size the total memory pool size in bytes 276 | * @param align alignment in bytes 277 | */ 278 | void init (int num_workers, long long size=20*MB, long long align=4096); 279 | 280 | /** 281 | * print information about memory pool for debugging purpose. 282 | */ 283 | void print(void); 284 | 285 | /** 286 | * print memory usage for each thread pool 287 | */ 288 | void print_usage(void); 289 | 290 | }; // threadMemPools 291 | 292 | /* -------------------------------------------------------------- */ 293 | 294 | /** 295 | * threadNVMPools allocates a contiguous region of NVM then divides it 296 | * among threads' individual mempools. 297 | */ 298 | class threadNVMPools { 299 | public: 300 | mempool *tm_pools; /* pools[0..num_workers-1] */ 301 | int tm_num_workers; 302 | 303 | char * tm_buf; /* start address of allocated memory */ 304 | long long tm_size; /* tm_buf size */ 305 | 306 | const char * tn_nvm_file; 307 | 308 | public: 309 | /** 310 | * constructor 311 | */ 312 | threadNVMPools() 313 | {tm_pools= NULL; tm_num_workers= 0; 314 | tm_buf= NULL; tm_size= 0; 315 | tn_nvm_file=NULL; 316 | } 317 | 318 | /** 319 | * destructor 320 | */ 321 | ~threadNVMPools(); 322 | 323 | /** 324 | * Initialize the NVM memory pools 325 | * allocate NVM and initialize all per-worker mempool. 326 | * 327 | * @param num_workers number of parallel worker threads 328 | * @param num_file the nvm file name to map 329 | * @param size the total memory pool size in bytes, must be multiple of 4KB 330 | */ 331 | void init (int num_workers, const char * nvm_file, long long size=20*MB); 332 | 333 | void print(void); 334 | 335 | /** 336 | * print NVM usage for each thread pool 337 | */ 338 | void print_usage(void); 339 | 340 | }; // threadNVMPools 341 | 342 | /* -------------------------------------------------------------- */ 343 | /* (1) call the_thread_mempool.init(...) 344 | * (2) In each thread that wants to use the mempool, 345 | * set worker_id 346 | */ 347 | extern thread_local int worker_id; /* in Thread Local Storage */ 348 | 349 | extern threadMemPools the_thread_mempools; 350 | extern threadNVMPools the_thread_nvmpools; 351 | 352 | #define the_mempool (the_thread_mempools.tm_pools[worker_id]) 353 | #define mempool_alloc the_mempool.alloc 354 | #define mempool_free the_mempool.free 355 | #define mempool_alloc_node the_mempool.alloc_node 356 | #define mempool_free_node the_mempool.free_node 357 | 358 | #define the_nvmpool (the_thread_nvmpools.tm_pools[worker_id]) 359 | #define nvmpool_alloc the_nvmpool.alloc 360 | #define nvmpool_free the_nvmpool.free 361 | #define nvmpool_alloc_node the_nvmpool.alloc_node 362 | #define nvmpool_free_node the_nvmpool.free_node 363 | 364 | /* -------------------------------------------------------------- */ 365 | #endif /* _BTREE_MEM_POOL_H */ 366 | -------------------------------------------------------------------------------- /common/nodepref.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file memdiff.h 3 | * @author Shimin Chen , Jihang Liu, Leying Chen 4 | * @version 1.0 5 | * 6 | * @section LICENSE 7 | * 8 | * TBD 9 | * 10 | * @section DESCRIPTION 11 | * 12 | * This file defines macros to prefetch btree nodes 13 | */ 14 | 15 | #ifndef _BTREE_NODEPREF_H 16 | #define _BTREE_NODEPREF_H 17 | 18 | /* --------------------------------------------------------------------- */ 19 | /* Prefetch Instructions */ 20 | /* --------------------------------------------------------------------- */ 21 | /* 22 | T0 (temporal data): 23 | prefetch data into all levels of the cache hierarchy. 24 | Pentium III processor 1st- or 2nd-level cache. 25 | Pentium 4 and Intel Xeon processors 2nd-level cache. 26 | 27 | NTA (non-temporal data with respect to all cache levels) 28 | prefetch data into non-temporal cache structure and 29 | into a location close to the processor, minimizing cache pollution. 30 | Pentium III processor 1st-level cache 31 | Pentium 4 and Intel Xeon processors 2nd-level cache 32 | */ 33 | 34 | // We use prefetch instructions by default. 35 | #ifndef NO_PREFETCH 36 | 37 | #define prefetcht0(mem_var) \ 38 | __asm__ __volatile__ ("prefetcht0 %0": :"m"(mem_var)) 39 | #define prefetchnta(mem_var) \ 40 | __asm__ __volatile__ ("prefetchnta %0": :"m"(mem_var)) 41 | 42 | #define pref(mem_var) prefetcht0(mem_var) 43 | #define prefnta(mem_var) prefetchnta(mem_var) 44 | 45 | #define ptouch(mem_var) \ 46 | __asm__ __volatile__ ("movl %0, %%eax": :"m"(mem_var):"%eax") 47 | 48 | #else // NO_PREFETCH 49 | 50 | #define pref(mem_var) 51 | #define prefnta(mem_var) 52 | #define ptouch(mem_var) 53 | 54 | #endif 55 | 56 | /* ---------------------------------------------------------------------- */ 57 | 58 | #ifndef KB 59 | #define KB (1024) 60 | #endif 61 | 62 | #ifndef MB 63 | #define MB (1024*KB) 64 | #endif 65 | 66 | #ifndef GB 67 | #define GB (1024*MB) 68 | #endif 69 | 70 | #define CACHE_LINE_SIZE 64 71 | #define PAGE_SIZE (4*KB) 72 | 73 | // obtain the starting address of a cache line 74 | #define getline(addr) \ 75 | (((unsigned long long)(addr)) & (~(unsigned long long)(CACHE_LINE_SIZE-1))) 76 | 77 | // check if address is aligned at line boundary 78 | #define isaligned_atline(addr) \ 79 | (!(((unsigned long long)(addr)) & (unsigned long long)(CACHE_LINE_SIZE-1))) 80 | 81 | /* --------------------------------------------------------------------- */ 82 | /* Prefetch constant number of lines */ 83 | /* --------------------------------------------------------------------- */ 84 | 85 | // e.g. PREF_16(pref, *p, L2_CACHE_LINE); 86 | 87 | #define PREF_1(cmd, mem_var) cmd(mem_var) 88 | 89 | #define PREF_2(cmd, mem_var) \ 90 | PREF_1(cmd, mem_var); cmd(*((char *)&(mem_var) + (CACHE_LINE_SIZE))) 91 | 92 | #define PREF_3(cmd, mem_var) \ 93 | PREF_2(cmd, mem_var); cmd(*((char *)&(mem_var) + 2*(CACHE_LINE_SIZE))) 94 | 95 | #define PREF_4(cmd, mem_var) \ 96 | PREF_3(cmd, mem_var); cmd(*((char *)&(mem_var) + 3*(CACHE_LINE_SIZE))) 97 | 98 | #define PREF_5(cmd, mem_var) \ 99 | PREF_4(cmd, mem_var); cmd(*((char *)&(mem_var) + 4*(CACHE_LINE_SIZE))) 100 | 101 | #define PREF_6(cmd, mem_var) \ 102 | PREF_5(cmd, mem_var); cmd(*((char *)&(mem_var) + 5*(CACHE_LINE_SIZE))) 103 | 104 | #define PREF_7(cmd, mem_var) \ 105 | PREF_6(cmd, mem_var); cmd(*((char *)&(mem_var) + 6*(CACHE_LINE_SIZE))) 106 | 107 | #define PREF_8(cmd, mem_var) \ 108 | PREF_7(cmd, mem_var); cmd(*((char *)&(mem_var) + 7*(CACHE_LINE_SIZE))) 109 | 110 | #define PREF_9(cmd, mem_var) \ 111 | PREF_8(cmd, mem_var); cmd(*((char *)&(mem_var) + 8*(CACHE_LINE_SIZE))) 112 | 113 | #define PREF_10(cmd, mem_var) \ 114 | PREF_9(cmd, mem_var); cmd(*((char *)&(mem_var) + 9*(CACHE_LINE_SIZE))) 115 | 116 | #define PREF_11(cmd, mem_var) \ 117 | PREF_10(cmd, mem_var); cmd(*((char *)&(mem_var) + 10*(CACHE_LINE_SIZE))) 118 | 119 | #define PREF_12(cmd, mem_var) \ 120 | PREF_11(cmd, mem_var); cmd(*((char *)&(mem_var) + 11*(CACHE_LINE_SIZE))) 121 | 122 | #define PREF_13(cmd, mem_var) \ 123 | PREF_12(cmd, mem_var); cmd(*((char *)&(mem_var) + 12*(CACHE_LINE_SIZE))) 124 | 125 | #define PREF_14(cmd, mem_var) \ 126 | PREF_13(cmd, mem_var); cmd(*((char *)&(mem_var) + 13*(CACHE_LINE_SIZE))) 127 | 128 | #define PREF_15(cmd, mem_var) \ 129 | PREF_14(cmd, mem_var); cmd(*((char *)&(mem_var) + 14*(CACHE_LINE_SIZE))) 130 | 131 | #define PREF_16(cmd, mem_var) \ 132 | PREF_15(cmd, mem_var); cmd(*((char *)&(mem_var) + 15*(CACHE_LINE_SIZE))) 133 | 134 | #define PREF_17(cmd, mem_var) \ 135 | PREF_16(cmd, mem_var); cmd(*((char *)&(mem_var) + 16*(CACHE_LINE_SIZE))) 136 | 137 | #define PREF_18(cmd, mem_var) \ 138 | PREF_17(cmd, mem_var); cmd(*((char *)&(mem_var) + 17*(CACHE_LINE_SIZE))) 139 | 140 | #define PREF_19(cmd, mem_var) \ 141 | PREF_18(cmd, mem_var); cmd(*((char *)&(mem_var) + 18*(CACHE_LINE_SIZE))) 142 | 143 | #define PREF_20(cmd, mem_var) \ 144 | PREF_19(cmd, mem_var); cmd(*((char *)&(mem_var) + 19*(CACHE_LINE_SIZE))) 145 | 146 | #define PREF_21(cmd, mem_var) \ 147 | PREF_20(cmd, mem_var); cmd(*((char *)&(mem_var) + 20*(CACHE_LINE_SIZE))) 148 | 149 | #define PREF_22(cmd, mem_var) \ 150 | PREF_21(cmd, mem_var); cmd(*((char *)&(mem_var) + 21*(CACHE_LINE_SIZE))) 151 | 152 | #define PREF_23(cmd, mem_var) \ 153 | PREF_22(cmd, mem_var); cmd(*((char *)&(mem_var) + 22*(CACHE_LINE_SIZE))) 154 | 155 | #define PREF_24(cmd, mem_var) \ 156 | PREF_23(cmd, mem_var); cmd(*((char *)&(mem_var) + 23*(CACHE_LINE_SIZE))) 157 | 158 | #define PREF_25(cmd, mem_var) \ 159 | PREF_24(cmd, mem_var); cmd(*((char *)&(mem_var) + 24*(CACHE_LINE_SIZE))) 160 | 161 | #define PREF_26(cmd, mem_var) \ 162 | PREF_25(cmd, mem_var); cmd(*((char *)&(mem_var) + 25*(CACHE_LINE_SIZE))) 163 | 164 | #define PREF_27(cmd, mem_var) \ 165 | PREF_26(cmd, mem_var); cmd(*((char *)&(mem_var) + 26*(CACHE_LINE_SIZE))) 166 | 167 | #define PREF_28(cmd, mem_var) \ 168 | PREF_27(cmd, mem_var); cmd(*((char *)&(mem_var) + 27*(CACHE_LINE_SIZE))) 169 | 170 | #define PREF_29(cmd, mem_var) \ 171 | PREF_28(cmd, mem_var); cmd(*((char *)&(mem_var) + 28*(CACHE_LINE_SIZE))) 172 | 173 | #define PREF_30(cmd, mem_var) \ 174 | PREF_29(cmd, mem_var); cmd(*((char *)&(mem_var) + 29*(CACHE_LINE_SIZE))) 175 | 176 | #define PREF_31(cmd, mem_var) \ 177 | PREF_30(cmd, mem_var); cmd(*((char *)&(mem_var) + 30*(CACHE_LINE_SIZE))) 178 | 179 | #define PREF_32(cmd, mem_var) \ 180 | PREF_31(cmd, mem_var); cmd(*((char *)&(mem_var) + 31*(CACHE_LINE_SIZE))) 181 | 182 | /* ---------------------------------------------------------------------- */ 183 | #ifndef NO_PREFETCH 184 | 185 | #define LOOP_PREF(cmd, mem_varp, nline) \ 186 | {char *_p= (char *)(mem_varp); \ 187 | char *_pend= _p + (nline)*CACHE_LINE_SIZE; \ 188 | for (; _p<_pend; _p+=(CACHE_LINE_SIZE)) cmd(*_p); \ 189 | } 190 | 191 | #else // NO_PREFETCH 192 | 193 | #define LOOP_PREF(cmd, mem_varp, nline) 194 | 195 | #endif 196 | 197 | /* ---------------------------------------------------------------------- */ 198 | 199 | // for non leaf node 200 | static void inline NODE_PREF(register void *bbp) 201 | { 202 | pref (* ((char *)bbp)); 203 | # if NONLEAF_LINE_NUM >= 2 204 | pref (* ((char *)bbp + CACHE_LINE_SIZE)); 205 | # endif 206 | # if NONLEAF_LINE_NUM >= 3 207 | pref (* ((char *)bbp + CACHE_LINE_SIZE*2)); 208 | # endif 209 | # if NONLEAF_LINE_NUM >= 4 210 | pref (* ((char *)bbp + CACHE_LINE_SIZE*3)); 211 | # endif 212 | # if NONLEAF_LINE_NUM >= 5 213 | pref (* ((char *)bbp + CACHE_LINE_SIZE*4)); 214 | # endif 215 | # if NONLEAF_LINE_NUM >= 6 216 | pref (* ((char *)bbp + CACHE_LINE_SIZE*5)); 217 | # endif 218 | # if NONLEAF_LINE_NUM >= 7 219 | pref (* ((char *)bbp + CACHE_LINE_SIZE*6)); 220 | # endif 221 | # if NONLEAF_LINE_NUM >= 8 222 | pref (* ((char *)bbp + CACHE_LINE_SIZE*7)); 223 | # endif 224 | # if NONLEAF_LINE_NUM >= 9 225 | pref (* ((char *)bbp + CACHE_LINE_SIZE*8)); 226 | # endif 227 | # if NONLEAF_LINE_NUM >= 10 228 | pref (* ((char *)bbp + CACHE_LINE_SIZE*9)); 229 | # endif 230 | # if NONLEAF_LINE_NUM >= 11 231 | pref (* ((char *)bbp + CACHE_LINE_SIZE*10)); 232 | # endif 233 | # if NONLEAF_LINE_NUM >= 12 234 | pref (* ((char *)bbp + CACHE_LINE_SIZE*11)); 235 | # endif 236 | # if NONLEAF_LINE_NUM >= 13 237 | pref (* ((char *)bbp + CACHE_LINE_SIZE*12)); 238 | # endif 239 | # if NONLEAF_LINE_NUM >= 14 240 | pref (* ((char *)bbp + CACHE_LINE_SIZE*13)); 241 | # endif 242 | # if NONLEAF_LINE_NUM >= 15 243 | pref (* ((char *)bbp + CACHE_LINE_SIZE*14)); 244 | # endif 245 | # if NONLEAF_LINE_NUM >= 16 246 | pref (* ((char *)bbp + CACHE_LINE_SIZE*15)); 247 | # endif 248 | # if NONLEAF_LINE_NUM >= 17 249 | pref (* ((char *)bbp + CACHE_LINE_SIZE*16)); 250 | # endif 251 | # if NONLEAF_LINE_NUM >= 18 252 | pref (* ((char *)bbp + CACHE_LINE_SIZE*17)); 253 | # endif 254 | # if NONLEAF_LINE_NUM >= 19 255 | pref (* ((char *)bbp + CACHE_LINE_SIZE*18)); 256 | # endif 257 | # if NONLEAF_LINE_NUM >= 20 258 | pref (* ((char *)bbp + CACHE_LINE_SIZE*19)); 259 | # endif 260 | # if NONLEAF_LINE_NUM >= 21 261 | pref (* ((char *)bbp + CACHE_LINE_SIZE*20)); 262 | # endif 263 | # if NONLEAF_LINE_NUM >= 22 264 | pref (* ((char *)bbp + CACHE_LINE_SIZE*21)); 265 | # endif 266 | # if NONLEAF_LINE_NUM >= 23 267 | pref (* ((char *)bbp + CACHE_LINE_SIZE*22)); 268 | # endif 269 | # if NONLEAF_LINE_NUM >= 24 270 | pref (* ((char *)bbp + CACHE_LINE_SIZE*23)); 271 | # endif 272 | # if NONLEAF_LINE_NUM >= 25 273 | pref (* ((char *)bbp + CACHE_LINE_SIZE*24)); 274 | # endif 275 | # if NONLEAF_LINE_NUM >= 26 276 | pref (* ((char *)bbp + CACHE_LINE_SIZE*25)); 277 | # endif 278 | # if NONLEAF_LINE_NUM >= 27 279 | pref (* ((char *)bbp + CACHE_LINE_SIZE*26)); 280 | # endif 281 | # if NONLEAF_LINE_NUM >= 28 282 | pref (* ((char *)bbp + CACHE_LINE_SIZE*27)); 283 | # endif 284 | # if NONLEAF_LINE_NUM >= 29 285 | pref (* ((char *)bbp + CACHE_LINE_SIZE*28)); 286 | # endif 287 | # if NONLEAF_LINE_NUM >= 30 288 | pref (* ((char *)bbp + CACHE_LINE_SIZE*29)); 289 | # endif 290 | # if NONLEAF_LINE_NUM >= 31 291 | pref (* ((char *)bbp + CACHE_LINE_SIZE*30)); 292 | # endif 293 | # if NONLEAF_LINE_NUM >= 32 294 | pref (* ((char *)bbp + CACHE_LINE_SIZE*31)); 295 | # endif 296 | # if NONLEAF_LINE_NUM >= 33 297 | # error "NONLEAF_LINE_NUM must be <= 32!" 298 | # endif 299 | } 300 | 301 | static void inline LEAF_PREF(register void *bbp) 302 | { 303 | pref (* ((char *)bbp)); 304 | # if LEAF_LINE_NUM >= 2 305 | pref (* ((char *)bbp + CACHE_LINE_SIZE)); 306 | # endif 307 | # if LEAF_LINE_NUM >= 3 308 | pref (* ((char *)bbp + CACHE_LINE_SIZE*2)); 309 | # endif 310 | # if LEAF_LINE_NUM >= 4 311 | pref (* ((char *)bbp + CACHE_LINE_SIZE*3)); 312 | # endif 313 | # if LEAF_LINE_NUM >= 5 314 | pref (* ((char *)bbp + CACHE_LINE_SIZE*4)); 315 | # endif 316 | # if LEAF_LINE_NUM >= 6 317 | pref (* ((char *)bbp + CACHE_LINE_SIZE*5)); 318 | # endif 319 | # if LEAF_LINE_NUM >= 7 320 | pref (* ((char *)bbp + CACHE_LINE_SIZE*6)); 321 | # endif 322 | # if LEAF_LINE_NUM >= 8 323 | pref (* ((char *)bbp + CACHE_LINE_SIZE*7)); 324 | # endif 325 | # if LEAF_LINE_NUM >= 9 326 | pref (* ((char *)bbp + CACHE_LINE_SIZE*8)); 327 | # endif 328 | # if LEAF_LINE_NUM >= 10 329 | pref (* ((char *)bbp + CACHE_LINE_SIZE*9)); 330 | # endif 331 | # if LEAF_LINE_NUM >= 11 332 | pref (* ((char *)bbp + CACHE_LINE_SIZE*10)); 333 | # endif 334 | # if LEAF_LINE_NUM >= 12 335 | pref (* ((char *)bbp + CACHE_LINE_SIZE*11)); 336 | # endif 337 | # if LEAF_LINE_NUM >= 13 338 | pref (* ((char *)bbp + CACHE_LINE_SIZE*12)); 339 | # endif 340 | # if LEAF_LINE_NUM >= 14 341 | pref (* ((char *)bbp + CACHE_LINE_SIZE*13)); 342 | # endif 343 | # if LEAF_LINE_NUM >= 15 344 | pref (* ((char *)bbp + CACHE_LINE_SIZE*14)); 345 | # endif 346 | # if LEAF_LINE_NUM >= 16 347 | pref (* ((char *)bbp + CACHE_LINE_SIZE*15)); 348 | # endif 349 | # if LEAF_LINE_NUM >= 17 350 | pref (* ((char *)bbp + CACHE_LINE_SIZE*16)); 351 | # endif 352 | # if LEAF_LINE_NUM >= 18 353 | pref (* ((char *)bbp + CACHE_LINE_SIZE*17)); 354 | # endif 355 | # if LEAF_LINE_NUM >= 19 356 | pref (* ((char *)bbp + CACHE_LINE_SIZE*18)); 357 | # endif 358 | # if LEAF_LINE_NUM >= 20 359 | pref (* ((char *)bbp + CACHE_LINE_SIZE*19)); 360 | # endif 361 | # if LEAF_LINE_NUM >= 21 362 | pref (* ((char *)bbp + CACHE_LINE_SIZE*20)); 363 | # endif 364 | # if LEAF_LINE_NUM >= 22 365 | pref (* ((char *)bbp + CACHE_LINE_SIZE*21)); 366 | # endif 367 | # if LEAF_LINE_NUM >= 23 368 | pref (* ((char *)bbp + CACHE_LINE_SIZE*22)); 369 | # endif 370 | # if LEAF_LINE_NUM >= 24 371 | pref (* ((char *)bbp + CACHE_LINE_SIZE*23)); 372 | # endif 373 | # if LEAF_LINE_NUM >= 25 374 | pref (* ((char *)bbp + CACHE_LINE_SIZE*24)); 375 | # endif 376 | # if LEAF_LINE_NUM >= 26 377 | pref (* ((char *)bbp + CACHE_LINE_SIZE*25)); 378 | # endif 379 | # if LEAF_LINE_NUM >= 27 380 | pref (* ((char *)bbp + CACHE_LINE_SIZE*26)); 381 | # endif 382 | # if LEAF_LINE_NUM >= 28 383 | pref (* ((char *)bbp + CACHE_LINE_SIZE*27)); 384 | # endif 385 | # if LEAF_LINE_NUM >= 29 386 | pref (* ((char *)bbp + CACHE_LINE_SIZE*28)); 387 | # endif 388 | # if LEAF_LINE_NUM >= 30 389 | pref (* ((char *)bbp + CACHE_LINE_SIZE*29)); 390 | # endif 391 | # if LEAF_LINE_NUM >= 31 392 | pref (* ((char *)bbp + CACHE_LINE_SIZE*30)); 393 | # endif 394 | # if LEAF_LINE_NUM >= 32 395 | pref (* ((char *)bbp + CACHE_LINE_SIZE*31)); 396 | # endif 397 | # if LEAF_LINE_NUM >= 33 398 | # error "LEAF_LINE_NUM must be <= 32!" 399 | # endif 400 | } 401 | 402 | #define NODE_PREF_ST NODE_PREF 403 | #define LEAF_PREF_ST LEAF_PREF 404 | 405 | /* ---------------------------------------------------------------------- */ 406 | #ifndef clear_cache 407 | 408 | #include 409 | #include 410 | #include 411 | 412 | static inline void software_clear_cache (void) 413 | {char *buf; 414 | int i; 415 | buf = (char *)memalign(CACHE_LINE_SIZE, 100*MB); 416 | if (buf == NULL) { 417 | perror ("malloc in clear_cache()"); 418 | exit (1); 419 | } 420 | for (i=0; i<10*MB; i+=CACHE_LINE_SIZE) /* write */ 421 | buf[i] = 'a'; 422 | for (i=0; i<10*MB; i+=CACHE_LINE_SIZE) /* write */ 423 | if (buf[i] != 'a') { 424 | printf ("Oops? clear_cache()\n"); 425 | exit(1); 426 | } 427 | free (buf); 428 | } 429 | 430 | #define clear_cache software_clear_cache 431 | 432 | #endif /* clear_cache */ 433 | 434 | /* ---------------------------------------------------------------------- */ 435 | #endif /* _BTREE_NODEPREF_H */ 436 | -------------------------------------------------------------------------------- /common/nvm-common.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * @file nvmlog-common.cc 3 | * @author Shimin Chen , Jihang Liu, Leying Chen 4 | * @version 1.0 5 | * 6 | * @section LICENSE 7 | * 8 | * TBD 9 | * 10 | * @section DESCRIPTION 11 | * 12 | * initliaze per-worker log 13 | */ 14 | 15 | #include "nvm-common.h" 16 | 17 | NvmLog *the_nvm_logs; 18 | 19 | void nvmLogInit(int num_workers) 20 | { 21 | the_nvm_logs = new NvmLog[num_workers]; 22 | if (!the_nvm_logs) { 23 | perror("new"); exit(1); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/performance.h: -------------------------------------------------------------------------------- 1 | /* File Name: performance.h 2 | * Author: Shimin Chen, Jihang Liu, Leying Chen 3 | */ 4 | 5 | #ifndef _BTREE_PERFORMANCE_H 6 | #define _BTREE_PERFORMANCE_H 7 | 8 | #include 9 | #include 10 | 11 | #define TEST_PERFORMANCE(ret, COMMAND) \ 12 | { struct timeval _start_tv, _end_tv; \ 13 | long long total_us; \ 14 | \ 15 | if (gettimeofday (&_start_tv, NULL) < 0) { \ 16 | perror ("get start time"); \ 17 | } \ 18 | \ 19 | COMMAND; \ 20 | \ 21 | if (gettimeofday (&_end_tv, NULL) < 0) { \ 22 | perror ("get end time"); \ 23 | } \ 24 | \ 25 | total_us = (_end_tv.tv_sec - _start_tv.tv_sec)*1000000\ 26 | +(_end_tv.tv_usec- _start_tv.tv_usec); \ 27 | printf ("elapsed time:%lld us\n", total_us); \ 28 | (ret) = total_us; \ 29 | } 30 | 31 | // ----------------------------------------------------------------------------- 32 | #endif /* _BTREE_PERFORMANCE_H */ 33 | -------------------------------------------------------------------------------- /common/tree.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tree.cc 3 | * @author Shimin Chen , Jihang Liu, Leying Chen 4 | * @version 1.0 5 | * 6 | * @section LICENSE 7 | * 8 | * TBD 9 | * 10 | * @section DESCRIPTION 11 | * 12 | * This file contains the main driver for experiments. 13 | */ 14 | 15 | #include "tree.h" 16 | 17 | /* ------------------------------------------------------------------------ */ 18 | /* global variables */ 19 | /* ------------------------------------------------------------------------ */ 20 | tree * the_treep= NULL; 21 | int worker_thread_num= 0; 22 | const char * nvm_file_name= NULL; 23 | bool debug_test= false; 24 | 25 | #ifdef INSTRUMENT_INSERTION 26 | int insert_total; // insert_total= 27 | int insert_no_split; // insert_no_split 28 | int insert_leaf_split; // +insert_leaf_split 29 | int insert_nonleaf_split;// +insert_nonleaf_split 30 | int total_node_splits; // an insertion may cause multiple node splits 31 | #endif // INSTRUMENT_INSERTION 32 | 33 | #ifdef NVMFLUSH_STAT 34 | NVMFLUSH_STAT_DEFS; 35 | #endif 36 | 37 | /* ------------------------------------------------------------------------ */ 38 | /* get keys from a key file of int keys */ 39 | /* ------------------------------------------------------------------------ */ 40 | static Int64 * getKeys (char *filename, Int64 num) 41 | { 42 | int fd; 43 | Int64 *p; 44 | fd = open (filename, 0, 0600); 45 | if (fd == -1) { 46 | perror (filename); exit(1); 47 | } 48 | 49 | p = (Int64 *) malloc ((num+1) * sizeof(Int64)); 50 | if (p == NULL) { 51 | printf ("malloc error\n"); exit (1); 52 | } 53 | // we allocate one more element at the end so that key[num] does not 54 | // cause segfault. This simplifies our program in main() 55 | 56 | if (read (fd, p, num*sizeof(Int64)) != num * sizeof(Int64)) 57 | {perror ("read"); exit (1);} 58 | 59 | close (fd); 60 | return p; 61 | } 62 | 63 | /* ------------------------------------------------------------------------ */ 64 | /* command line usage */ 65 | /* ------------------------------------------------------------------------ */ 66 | static void usage (char *cmd) 67 | { 68 | fprintf (stderr, 69 | "Usage: %s [ ] ... \n" 70 | "--------------------------------------------------\n" 71 | "[Initialization]\n" 72 | " thread must be the first command, followed by mempool and nvmpool.\n\n" 73 | " thread \n" 74 | " mempool \n" 75 | " nvmpool \n" 76 | "--------------------------------------------------\n" 77 | "[Debugging]\n" 78 | " use these commands to test the correctness of the implementation\n\n" 79 | " debug_bulkload \n" 80 | " debug_randomize \n" 81 | " debug_lookup \n" 82 | " debug_insert \n" 83 | " debug_del \n" 84 | "--------------------------------------------------\n" 85 | "[Test Preparation]\n" 86 | " prepare a tree before performance tests\n\n" 87 | " bulkload \n" 88 | " randomize\n" 89 | " stable \n" 90 | "--------------------------------------------------\n" 91 | "[Performance Tests]\n" 92 | " measure performance of various tree operations\n\n" 93 | " lookup \n" 94 | " insert \n" 95 | " del \n" 96 | "--------------------------------------------------\n" 97 | "[Misc]\n" 98 | " helper commands. debug_test enables correctness check for performance tests.\n\n" 99 | " print_tree\n" 100 | " check_tree\n" 101 | " print_mem\n" 102 | " debug_test\n" 103 | " sleep \n" 104 | "--------------------------------------------------\n" 105 | , cmd); 106 | exit (1); 107 | } 108 | 109 | int compar_void_ptr(const void *p1, const void *p2) 110 | { 111 | long *v1 = (long *)p1; 112 | long *v2 = (long *)p2; 113 | long t= (*v1 - *v2); 114 | 115 | return (t>0 ? 1 : (t<0 ? -1 : 0)); 116 | } 117 | 118 | /** 119 | * The test run for lookup operations 120 | */ 121 | static inline int lookupTest(Int64 key[], int start, int end) 122 | { 123 | int found= 0; 124 | 125 | for (int ii=start; iilookup (key[ii], &pos); 130 | 131 | if (debug_test) { 132 | if (pos >= 0) { // found 133 | void * recptr = the_treep->get_recptr (p, pos); 134 | assert ((key_type)recptr == key[ii]); // ptr == key in test prep 135 | found ++; 136 | } 137 | } 138 | 139 | } // end of for 140 | 141 | return found; 142 | } 143 | 144 | /** 145 | * The test run for insert operations 146 | */ 147 | static inline int insertTest(Int64 key[], int start, int end) 148 | { 149 | int found= 0; 150 | 151 | for (int ii=start; iiinsert (kk, (void *) kk); 154 | } // end of for 155 | 156 | if (debug_test) { 157 | for (int ii=start; iilookup (key[ii], &pos); 161 | found += (pos >= 0); 162 | } 163 | } 164 | 165 | return found; 166 | } 167 | 168 | /** 169 | * The test run for deletion operations 170 | */ 171 | static inline int delTest(Int64 key[], int start, int end) 172 | { 173 | int found= 0; 174 | 175 | for (int ii=start; iidel (kk); 178 | } // end of for 179 | 180 | if (debug_test) { 181 | for (int ii=start; iilookup (key[ii], &pos); 185 | found += (pos >= 0); 186 | } 187 | } 188 | 189 | return found; 190 | } 191 | 192 | 193 | int parse_command (int argc, char **argv) 194 | { 195 | if (argc < 2) usage (argv[0]); 196 | char * cmd = argv[0]; 197 | argc --; argv ++; 198 | 199 | while (argc > 0) { 200 | 201 | // ***************************************************************** 202 | // Initialization 203 | // ***************************************************************** 204 | 205 | // --- 206 | // thread 207 | // --- 208 | if (strcmp(argv[0],"thread") == 0){ 209 | if(argc < 2) usage(cmd); 210 | 211 | worker_thread_num = atoi(argv[1]); 212 | worker_id= 0; // the main thread will use worker[0]'s mem/nvm pool 213 | 214 | printf("number of worker threads is %d\n", worker_thread_num); 215 | argc -= 2; argv += 2; 216 | } 217 | 218 | // --- 219 | // mempool 220 | // --- 221 | else if (strcmp (argv[0], "mempool") == 0) { 222 | // get params 223 | if (argc < 2) usage (cmd); 224 | long long size = atoi(argv[1]); 225 | size *= MB; 226 | argc -= 2; argv += 2; 227 | 228 | if (worker_thread_num <= 0) { 229 | fprintf(stderr, "need to set worker_thread_num first!\n"); 230 | exit(1); 231 | } 232 | 233 | // initialize mempool per worker thread 234 | the_thread_mempools.init(worker_thread_num, size, 4096); 235 | } 236 | 237 | // --- 238 | // nvmpool 239 | // --- 240 | else if(strcmp(argv[0], "nvmpool") == 0){ 241 | // get params 242 | if(argc < 3) usage(cmd); 243 | nvm_file_name = argv[1]; 244 | long long size = atoi(argv[2]); 245 | size *= MB; 246 | argc -= 3; argv += 3; 247 | 248 | if (worker_thread_num <= 0) { 249 | fprintf(stderr, "need to set worker_thread_num first!\n"); 250 | exit(1); 251 | } 252 | 253 | // initialize nvm pool and log per worker thread 254 | the_thread_nvmpools.init(worker_thread_num, nvm_file_name, size); 255 | 256 | // allocate a 4KB page for the tree in worker 0's pool 257 | char *nvm_addr= (char *)nvmpool_alloc(4*KB); 258 | the_treep= initTree(nvm_addr, false); 259 | 260 | // log may not be necessary for some tree implementations 261 | // For simplicity, we just initialize logs. This cost is low. 262 | nvmLogInit(worker_thread_num); 263 | } 264 | 265 | // ***************************************************************** 266 | // Misc 267 | // ***************************************************************** 268 | 269 | // --- 270 | // print_tree 271 | // --- 272 | else if (strcmp (argv[0], "print_tree") == 0) { 273 | // get params 274 | argc -= 1; argv += 1; 275 | 276 | the_treep->print (); 277 | } 278 | 279 | // --- 280 | // check_tree 281 | // --- 282 | else if (strcmp (argv[0], "check_tree") == 0) { 283 | // get params 284 | argc -= 1; argv += 1; 285 | 286 | key_type start, end; 287 | the_treep->check (&start, &end); 288 | printf("Check tree structure OK\n"); 289 | } 290 | 291 | // --- 292 | // print_mem 293 | // --- 294 | else if (strcmp (argv[0], "print_mem") == 0) { 295 | // get params 296 | argc -= 1; argv += 1; 297 | 298 | the_thread_mempools.print_usage(); 299 | the_thread_nvmpools.print_usage(); 300 | } 301 | 302 | // --- 303 | // debug_test 304 | // --- 305 | else if (strcmp (argv[0], "debug_test") == 0) { 306 | // get params 307 | argc -= 1; argv += 1; 308 | 309 | debug_test= true; 310 | } 311 | 312 | // --- 313 | // sleep 314 | // --- 315 | else if (strcmp (argv[0], "sleep") == 0) { 316 | // get params 317 | if (argc < 2) usage (cmd); 318 | int seconds= atoi(argv[1]); 319 | argc -= 2; argv += 2; 320 | 321 | printf("sleep %d seconds\n", seconds); 322 | sleep(seconds); 323 | } 324 | 325 | // ***************************************************************** 326 | // Debugging 327 | // ***************************************************************** 328 | 329 | // --- 330 | // debug_bulkload 331 | // --- 332 | else if (strcmp (argv[0], "debug_bulkload") == 0) { 333 | // get params 334 | if (argc < 3) usage (cmd); 335 | int keynum = atoi (argv[1]); 336 | float bfill; sscanf (argv[2], "%f", &bfill); 337 | argc -= 3; argv += 3; 338 | 339 | // initiate keys 340 | simpleKeyInput *input = new simpleKeyInput(2*keynum, 0, 2); 341 | 342 | // bulkload then check 343 | int level = the_treep->bulkload (keynum, input, bfill); 344 | printf ("root is at %d level\n", level); 345 | 346 | key_type start, end; 347 | the_treep->check (&start, &end); 348 | 349 | assert ((start == input->get_key(0)) 350 | && (end == input->get_key(keynum-1))); 351 | 352 | // free keys 353 | delete input; 354 | 355 | printf ("bulkload is good!\n"); 356 | } 357 | 358 | // --- 359 | // debug_randomize 360 | // --- 361 | else if (strcmp (argv[0], "debug_randomize") == 0) { 362 | // get params 363 | if (argc < 3) usage (cmd); 364 | int keynum = atoi (argv[1]); 365 | float bfill; sscanf (argv[2], "%f", &bfill); 366 | argc -= 3; argv += 3; 367 | 368 | // initiate keys 369 | simpleKeyInput *input = new simpleKeyInput(2*keynum, 0, 2); 370 | 371 | // bulkload then check 372 | int level = the_treep->bulkload (keynum, input, bfill); 373 | printf ("root is at %d level\n", level); 374 | the_treep->randomize(); // randomize a sorted tree 375 | the_treep->randomize(); // randomize an already random tree 376 | 377 | key_type start, end; 378 | the_treep->check (&start, &end); 379 | 380 | assert ((start == input->get_key(0)) 381 | && (end == input->get_key(keynum-1))); 382 | 383 | // free keys 384 | delete input; 385 | 386 | printf ("randomize is good!\n"); 387 | } 388 | 389 | // --- 390 | // debug_lookup 391 | // --- 392 | else if (strcmp (argv[0], "debug_lookup") == 0) { 393 | // get params 394 | if (argc < 3) usage (cmd); 395 | int keynum = atoi (argv[1]); 396 | float bfill; sscanf (argv[2], "%f", &bfill); 397 | argc -= 3; argv += 3; 398 | 399 | // initiate keys 400 | inMemKeyInput *input = new inMemKeyInput(2*keynum, 1, 2); 401 | 402 | // bulkload then check 403 | int level = the_treep->bulkload (keynum, input, bfill); 404 | the_treep->randomize(); 405 | 406 | key_type start, end; 407 | the_treep->check (&start, &end); 408 | 409 | assert ((start == input->keys[1]) 410 | && (end == input->keys[2*keynum-1])); 411 | 412 | // check look up 413 | for (int ii=0; iikeys[2*ii]; 418 | p = the_treep->lookup (kk, &pos); 419 | if (pos >= 1) 420 | assert ((key_type)(the_treep->get_recptr (p, pos)) != kk); 421 | 422 | kk= input->keys[2*ii+1]; 423 | p = the_treep->lookup (kk, &pos); 424 | assert ((key_type)(the_treep->get_recptr (p, pos)) == kk); 425 | } 426 | 427 | printf ("lookup is good!\n"); 428 | } 429 | 430 | // --- 431 | // debug_insert 432 | // --- 433 | else if (strcmp (argv[0], "debug_insert") == 0) { 434 | // get params 435 | if (argc < 2) usage (cmd); 436 | int keynum = atoi (argv[1]); 437 | float bfill=1.0; 438 | argc -= 2; argv += 2; 439 | 440 | printf("test 1\n"); 441 | 442 | {// initiate keys 443 | inMemKeyInput *input = new inMemKeyInput(2*keynum, 1, 2); 444 | 445 | // bulkload 1 key 446 | int level = the_treep->bulkload (1, input, bfill); 447 | 448 | // insertion: keynum-1 keys 449 | int keys_per_thread= floor(keynum-1, worker_thread_num); 450 | 451 | // run tests with multiple threads 452 | std::thread threads[worker_thread_num]; 453 | for (int t=0; tkeys[2*ii+1]; 461 | the_treep->insert (kk, (void *) kk); 462 | } 463 | }); 464 | } 465 | for (int t=0; tcheck (&start, &end); 470 | assert ((start == input->keys[1]) 471 | && (end == input->keys[2*keynum-1])); 472 | 473 | for (int ii=0; iikeys[2*ii]; 479 | p = the_treep->lookup (kk, &pos); 480 | if (pos >= 1) 481 | assert ((key_type)(the_treep->get_recptr (p, pos)) != kk); 482 | 483 | kk= input->keys[2*ii+1]; 484 | p = the_treep->lookup (kk, &pos); 485 | assert ((key_type)(the_treep->get_recptr (p, pos)) == kk); 486 | } 487 | 488 | delete input; 489 | } 490 | 491 | printf("test 2\n"); 492 | 493 | {// initiate keys 494 | inMemKeyInput *input = new inMemKeyInput(2*keynum, 1, 2); 495 | 496 | // Hmm... we have not reclaimed the space of the old tree. 497 | // But it's ok for debugging. 498 | 499 | // bulkload odd keys 500 | int level = the_treep->bulkload (keynum, input, bfill); 501 | 502 | // randomize the leaf nodes 503 | the_treep->randomize(); 504 | 505 | // insertion: keynum even keys 506 | int keys_per_thread= floor(keynum, worker_thread_num); 507 | 508 | // run tests with multiple threads 509 | std::thread threads[worker_thread_num]; 510 | for (int t=0; tkeys[2*ii]; 518 | the_treep->insert (kk, (void *) kk); 519 | } 520 | }); 521 | } 522 | for (int t=0; tcheck (&start, &end); 527 | assert ((start == input->keys[0]) 528 | && (end == input->keys[2*keynum-1])); 529 | 530 | // duplicate insertions: insert keys again 531 | for (int t=0; tkeys[2*ii]; 539 | the_treep->insert (kk, (void *) kk); 540 | } 541 | }); 542 | } 543 | for (int t=0; tcheck (&start, &end); 548 | 549 | assert ((start == input->keys[0]) 550 | && (end == input->keys[2*keynum-1])); 551 | 552 | for (int ii=0; iikeys[2*ii]; 558 | p = the_treep->lookup (kk, &pos); 559 | assert ((key_type)(the_treep->get_recptr (p, pos)) == kk); 560 | 561 | kk= input->keys[2*ii+1]; 562 | p = the_treep->lookup (kk, &pos); 563 | assert ((key_type)(the_treep->get_recptr (p, pos)) == kk); 564 | } 565 | } 566 | 567 | printf ("insertion is good!\n"); 568 | } 569 | 570 | // --- 571 | // debug_del 572 | // --- 573 | else if (strcmp (argv[0], "debug_del") == 0) { 574 | // get params 575 | if (argc < 2) usage (cmd); 576 | int keynum = atoi (argv[1]); 577 | float bfill=1.0; 578 | argc -= 2; argv += 2; 579 | 580 | if (keynum < 10) keynum = 10; 581 | 582 | // initiate keys 583 | inMemKeyInput *input = new inMemKeyInput(keynum, 0, 1); 584 | 585 | // bulkload 586 | int level = the_treep->bulkload (keynum, input, bfill); 587 | 588 | // randomize the leaf nodes 589 | the_treep->randomize(); 590 | 591 | // delete half of the keys 592 | {int range= floor(keynum, worker_thread_num); 593 | if (range % 2 ==1) range=range-1; // ensure range is even number 594 | 595 | std::thread threads[worker_thread_num]; 596 | for (int t=0; tkeys[ii]; 603 | the_treep->del (kk); 604 | } 605 | }); 606 | } 607 | for (int t=0; tcheck (&start, &end); 612 | 613 | // duplicate deletions: do the same deletions again 614 | {int range= floor(keynum, worker_thread_num); 615 | if (range % 2 ==1) range=range-1; // ensure range is even number 616 | 617 | std::thread threads[worker_thread_num]; 618 | for (int t=0; tkeys[ii]; 625 | the_treep->del (kk); 626 | } 627 | }); 628 | } 629 | for (int t=0; tcheck (&start, &end); 634 | 635 | // delete almost all the keys 636 | // from right 637 | int skey; 638 | int ekey = ((keynum-1) | 0x1); // odd keys 639 | int step = keynum/8; 640 | for (skey=keynum*3/4; skey>=keynum/2+2; skey -= step, step=(step>2?(step/2):2)) { 641 | 642 | // delete ekey, ekey-2, ekey-4, ... > skey 643 | {int range= floor(ekey-skey, worker_thread_num); 644 | if (range % 2 ==1) range=range-1; // ensure range is even number 645 | 646 | std::thread threads[worker_thread_num]; 647 | for (int t=0; tstart; ii-=2) { 653 | key_type kk= input->keys[ii]; 654 | the_treep->del (kk); 655 | } 656 | }); 657 | } 658 | for (int t=0; tcheck (&start, &end); 664 | assert ((start<=input->keys[1]) && (end >= input->keys[ekey])); 665 | } 666 | 667 | // from left 668 | step = keynum/8; 669 | skey = 1; 670 | for (ekey=keynum/4; ekey<=keynum/2-2; ekey += step, step=(step>2?(step/2):2)) { 671 | // delete skey, skey+2, skey+4, ... , keys[ii]; 683 | the_treep->del (kk); 684 | } 685 | }); 686 | } 687 | for (int t=0; tcheck (&start, &end); 693 | assert (start<=input->keys[skey]); 694 | } 695 | 696 | printf ("delete is good!\n"); 697 | } 698 | 699 | // ***************************************************************** 700 | // Test Preparation 701 | // ***************************************************************** 702 | 703 | // --- 704 | // bulkload 705 | // --- 706 | else if (strcmp (argv[0], "bulkload") == 0) { 707 | // get params 708 | if (argc < 4) usage (cmd); 709 | int keynum = atoi (argv[1]); 710 | char *keyfile = argv[2]; 711 | float bfill; sscanf (argv[3], "%f", &bfill); 712 | argc -= 4; argv += 4; 713 | 714 | printf ("-- bulkload %d %s %f\n", keynum, keyfile, bfill); 715 | 716 | // Input 717 | bufferedKeyInput *input = new bufferedKeyInput(keyfile, 0, keynum); 718 | 719 | // bulkload then check 720 | int level = the_treep->bulkload (keynum, input, bfill); 721 | printf ("root is at %d level\n", level); 722 | 723 | key_type start, end; 724 | the_treep->check (&start, &end); 725 | 726 | // free keys 727 | delete input; 728 | } 729 | 730 | // --- 731 | // randomize 732 | // --- 733 | else if (strcmp (argv[0], "randomize") == 0) { 734 | argc -= 1; argv += 1; 735 | 736 | printf ("-- randomize\n"); 737 | the_treep->randomize(); 738 | 739 | key_type start, end; 740 | the_treep->check (&start, &end); 741 | } 742 | 743 | // --- 744 | // stable 745 | // --- 746 | else if (strcmp (argv[0], "stable") == 0) { 747 | // get params 748 | if (argc < 3) usage (cmd); 749 | int keynum = atoi (argv[1]); 750 | char *keyfile = argv[2]; 751 | argc -= 3; argv += 3; 752 | 753 | printf ("-- stable %d %s\n", keynum, keyfile); 754 | 755 | // Input 756 | bufferedKeyInput *input = new bufferedKeyInput(keyfile, 0, keynum); 757 | 758 | // the keyfile is specially prepared for stable operation 759 | // the first 1/10 of the file is sorted 760 | // the rest of 9/10 is random 761 | 762 | // bulkload 10% of the keys 763 | int bulkload_num = keynum/10; 764 | int level = the_treep->bulkload (bulkload_num, input, 1.0); 765 | printf ("After bulkloading %d keys, level is %d\n", 766 | bulkload_num, level); 767 | 768 | // insertion: [bulkload_num, keynum-1], keynum-bulkload_num keys 769 | int range= floor(keynum-bulkload_num, worker_thread_num); 770 | 771 | // run tests with multiple threads 772 | std::thread threads[worker_thread_num]; 773 | for (int t=0; topenCursor(start, end-start); 780 | for (int ii=start; iiget_key(ii); 782 | the_treep->insert (kk, (void *) kk); 783 | } 784 | input->closeCursor(cursor); 785 | }); 786 | } 787 | for (int t=0; tcheck (&start, &end); 791 | 792 | printf ("root is at %d level\n", the_treep->level()); 793 | 794 | // free keys 795 | delete input; 796 | } 797 | 798 | 799 | // ***************************************************************** 800 | // Performance Tests 801 | // ***************************************************************** 802 | 803 | // --- 804 | // lookup 805 | // --- 806 | else if (strcmp (argv[0], "lookup") == 0) { 807 | // get params 808 | if (argc < 3) usage (cmd); 809 | int keynum = atoi (argv[1]); 810 | char *keyfile = argv[2]; 811 | argc -= 3; argv += 3; 812 | 813 | printf ("-- lookup %d %s\n", keynum, keyfile); 814 | 815 | // load keys from the file into an array in memory 816 | Int64 * key = getKeys (keyfile, keynum); 817 | 818 | // test 819 | unsigned long long total_us= 0; 820 | 821 | std::thread threads[worker_thread_num]; 822 | int range= floor(keynum, worker_thread_num); 823 | std::atomic found; 824 | found= 0; 825 | 826 | clear_cache (); 827 | 828 | TEST_PERFORMANCE(total_us, do { 829 | if (worker_thread_num > 1) { 830 | for (int t=0; t 1 841 | 842 | // worker_thread_num == 1 843 | else { 844 | found= lookupTest(key, 0, keynum); 845 | } 846 | }while(0)) 847 | 848 | if (debug_test) { 849 | printf ("lookup is good!\n"); 850 | printf("found %d keys\n", found.load()); 851 | } 852 | 853 | free (key); 854 | } 855 | 856 | // --- 857 | // insert 858 | // --- 859 | else if (strcmp (argv[0], "insert") == 0) { 860 | // get params 861 | if (argc < 3) usage (cmd); 862 | int keynum = atoi (argv[1]); 863 | char *keyfile = argv[2]; 864 | argc -= 3; argv += 3; 865 | 866 | printf ("-- insert %d %s\n", keynum, keyfile); 867 | 868 | // get keys from the file 869 | Int64 * key = getKeys (keyfile, keynum); 870 | 871 | // test 872 | unsigned long long total_us= 0; 873 | 874 | std::thread threads[worker_thread_num]; 875 | int range= floor(keynum, worker_thread_num); 876 | std::atomic found; 877 | found= 0; 878 | 879 | clear_cache (); 880 | 881 | #ifdef NVMFLUSH_STAT 882 | NVMFLUSH_STAT_init(); 883 | #endif 884 | 885 | TEST_PERFORMANCE(total_us, do { 886 | if (worker_thread_num > 1) { 887 | for (int t=0; t 1 898 | 899 | // worker_thread_num == 1 900 | else { 901 | found= insertTest(key, 0, keynum); 902 | } 903 | }while(0)) 904 | 905 | #ifdef NVMFLUSH_STAT 906 | NVMFLUSH_STAT_print(); 907 | #endif 908 | 909 | if (debug_test) { 910 | 911 | printf ("Insert %d keys / %d keys\n", found.load(), keynum); 912 | 913 | key_type start, end; 914 | the_treep->check (&start, &end); 915 | 916 | if (found.load() == keynum) { 917 | printf ("Insertion is good!\n"); 918 | } 919 | else { 920 | printf ("%d keys are not successfully inserted!\n", keynum - found.load()); 921 | } 922 | } 923 | 924 | free (key); 925 | } 926 | 927 | // --- 928 | // del 929 | // --- 930 | else if (strcmp (argv[0], "del") == 0) { 931 | // get params 932 | if (argc < 3) usage (cmd); 933 | int keynum = atoi (argv[1]); 934 | char *keyfile = argv[2]; 935 | argc -= 3; argv += 3; 936 | 937 | printf ("-- del %d %s\n", keynum, keyfile); 938 | 939 | // get keys from the file 940 | Int64 * key = getKeys (keyfile, keynum); 941 | 942 | // test 943 | unsigned long long total_us= 0; 944 | 945 | std::thread threads[worker_thread_num]; 946 | int range= floor(keynum, worker_thread_num); 947 | std::atomic found; 948 | found= 0; 949 | 950 | clear_cache (); 951 | 952 | #ifdef NVMFLUSH_STAT 953 | NVMFLUSH_STAT_init(); 954 | #endif 955 | 956 | TEST_PERFORMANCE(total_us, do { 957 | if (worker_thread_num > 1) { 958 | for (int t=0; t 1 969 | 970 | // worker_thread_num == 1 971 | else { 972 | found= delTest(key, 0, keynum); 973 | } 974 | }while(0)) 975 | 976 | #ifdef NVMFLUSH_STAT 977 | NVMFLUSH_STAT_print(); 978 | #endif 979 | 980 | if (debug_test) { 981 | 982 | key_type start, end; 983 | the_treep->check (&start, &end); 984 | 985 | if (found.load() == 0) { 986 | printf ("Deletion is good!\n"); 987 | } 988 | else { 989 | printf ("%d keys are not successfully deleted!\n", found.load()); 990 | } 991 | } 992 | 993 | free (key); 994 | } 995 | 996 | else { 997 | fprintf (stderr, "Unknown command: %s\n", argv[0]); 998 | usage (cmd); 999 | } 1000 | } // end of while 1001 | 1002 | return 0; 1003 | } 1004 | 1005 | -------------------------------------------------------------------------------- /common/tree.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @file tree.h 4 | * @author Shimin Chen , Jihang Liu, Leying Chen 5 | * @version 1.0 6 | * 7 | * @section LICENSE 8 | * 9 | * TBD 10 | * 11 | * @section DESCRIPTION 12 | * 13 | * The tree class defines the methods of trees. 14 | */ 15 | 16 | #ifndef _BTREE_TREE_H 17 | #define _BTREE_TREE_H 18 | /* ---------------------------------------------------------------------- */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | /* ---------------------------------------------------------------------- */ 29 | /* Default Parameters */ 30 | /* ---------------------------------------------------------------------- */ 31 | 32 | // the size of a tree node 33 | #define NONLEAF_LINE_NUM 4 // 256B 34 | #define LEAF_LINE_NUM 4 // 256B 35 | 36 | // the number of leaf nodes to prefetch ahead in jump pointer array 37 | // prefetching 38 | #ifndef PREFETCH_NUM_AHEAD 39 | #define PREFETCH_NUM_AHEAD 3 40 | #endif 41 | 42 | /* ---------------------------------------------------------------------- */ 43 | /* Node Size, Key Size, and Pointer Size */ 44 | /* ---------------------------------------------------------------------- */ 45 | 46 | // node size 47 | #define NONLEAF_SIZE (CACHE_LINE_SIZE * NONLEAF_LINE_NUM) 48 | #define LEAF_SIZE (CACHE_LINE_SIZE * LEAF_LINE_NUM) 49 | 50 | // key size and pointer size: 8B 51 | typedef long long key_type; 52 | #define KEY_SIZE 8 /* size of a key in tree node */ 53 | #define POINTER_SIZE 8 /* size of a pointer/value in node */ 54 | #define ITEM_SIZE 8 /* key size or pointer size */ 55 | 56 | #define MAX_KEY ((key_type)(0x7fffffffffffffffULL)) 57 | #define MIN_KEY ((key_type)(0x8000000000000000ULL)) 58 | 59 | #include "keyinput.h" 60 | #include "nodepref.h" 61 | #include "mempool.h" 62 | #include "nvm-common.h" 63 | #include "performance.h" 64 | 65 | /* ---------------------------------------------------------------------- */ 66 | /* Useful funcions */ 67 | /* ---------------------------------------------------------------------- */ 68 | /* GCC builtin functions 69 | 70 | int __builtin_ffs (unsigned int x) 71 | Returns one plus the index of the least significant 1-bit of x, or if x is 72 | zero, returns zero. 73 | 74 | int __builtin_popcount (unsigned int x) 75 | Returns the number of 1-bits in x. 76 | 77 | */ 78 | 79 | #define bitScan(x) __builtin_ffs(x) 80 | #define countBit(x) __builtin_popcount(x) 81 | 82 | static inline unsigned char hashcode1B(key_type x) { 83 | x ^= x>>32; 84 | x ^= x>>16; 85 | x ^= x>>8; 86 | return (unsigned char)(x&0x0ffULL); 87 | } 88 | 89 | static inline unsigned long long rdtsc(void) 90 | { 91 | unsigned hi, lo; 92 | __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); 93 | return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); 94 | } 95 | 96 | #define min(x,y) ((x)<=(y) ? (x) : (y)) 97 | #define max(x,y) ((x)<=(y) ? (y) : (x)) 98 | 99 | // compute ceiling(x/y) and floor(x/y) 100 | #define ceiling(x, y) (((x) + (y) - 1) / (y)) 101 | #define floor(x, y) ((x) / (y)) 102 | 103 | #define swap(x, y) \ 104 | do { auto _t=(x); (x)=(y); (y)=_t; } while(0) 105 | 106 | /* ---------------------------------------------------------------------- */ 107 | class tree { 108 | public: 109 | 110 | /** 111 | * bulkload a tree 112 | * 113 | * @param keynum number of keys to bulkload 114 | * @param input the keyInput structure that contains the keys 115 | * @param bfill the fill factor, which is a float in (0,1] 116 | * @return the number of tree levels 117 | */ 118 | virtual int bulkload (int keynum, keyInput *input, float bfill) 119 | { 120 | fprintf (stderr, "Not implemented!\n"); 121 | exit (1); 122 | return 0; 123 | } 124 | 125 | /** 126 | * randomize the key orders in nodes (only for unsorted/bitmap trees) 127 | */ 128 | virtual void randomize() {} 129 | 130 | /** 131 | * given a search key, perform the search operation 132 | * 133 | * @param key the search key 134 | * @param pos the position to return 135 | * @return the leaf node to return 136 | * 137 | * If a match to the given search key is found, then the leaf node and the 138 | * matching key position is returned. If a match is not found, then the leaf 139 | * node and the position to a previous key is returned. 140 | */ 141 | virtual void * lookup (key_type key, int *pos) 142 | { 143 | fprintf (stderr, "Not implemented!\n"); 144 | exit (1); 145 | return NULL; 146 | } 147 | 148 | /** 149 | * obtain the record pointer 150 | * 151 | * @param p leaf node pointer 152 | * @param pos index entry position in the leaf node 153 | * @return the associated record pointer 154 | */ 155 | virtual void * get_recptr (void *p, int pos) 156 | { 157 | fprintf (stderr, "Not implemented!\n"); 158 | exit (1); 159 | return NULL; 160 | } 161 | 162 | /** 163 | * insert an index entry 164 | * 165 | * @param key the index key 166 | * @param ptr the record pointer 167 | */ 168 | virtual void insert (key_type key, void * ptr) 169 | { 170 | fprintf (stderr, "Not implemented!\n"); 171 | exit (1); 172 | } 173 | 174 | /** 175 | * delete an index entry 176 | * 177 | * @param key the index key to delete 178 | */ 179 | virtual void del (key_type key) 180 | { 181 | fprintf (stderr, "Not implemented!\n"); 182 | exit (1); 183 | } 184 | 185 | /** 186 | * print the tree structure 187 | */ 188 | virtual void print () 189 | { 190 | fprintf (stderr, "Not implemented!\n"); 191 | exit (1); 192 | } 193 | 194 | /** 195 | * check the correctness of the tree structure 196 | * 197 | * @param start the start key of the tree 198 | * @param end the end key of the tree 199 | */ 200 | virtual void check (key_type *start, key_type *end) 201 | { 202 | fprintf (stderr, "Not implemented!\n"); 203 | exit (1); 204 | } 205 | 206 | /** 207 | * obtain the level parameter of the tree 208 | */ 209 | virtual int level () 210 | { 211 | fprintf (stderr, "Not implemented!\n"); 212 | exit (1); 213 | return 0; 214 | } 215 | 216 | }; // tree 217 | 218 | /* ---------------------------------------------------------------------- */ 219 | extern tree * the_treep; 220 | extern int worker_thread_num; 221 | extern const char * nvm_file_name; 222 | 223 | extern int parse_command (int argc, char **argv); 224 | 225 | #ifdef INSTRUMENT_INSERTION 226 | extern int insert_total; // insert_total= 227 | extern int insert_no_split; // insert_no_split 228 | extern int insert_leaf_split; // +insert_leaf_split 229 | extern int insert_nonleaf_split;// +insert_nonleaf_split 230 | extern int total_node_splits; // an insertion may cause multiple node splits 231 | #endif // INSTRUMENT_INSERTION 232 | 233 | // a specific tree should implement a child class of tree 234 | // and the following function 235 | extern tree * initTree(void *nvm_addr, bool recover); 236 | 237 | /* ---------------------------------------------------------------------- */ 238 | #endif /* _BTREE_TREE_H */ 239 | -------------------------------------------------------------------------------- /keygen-8B/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -O2 -g -D_FILE_OFFSET_BITS=64 -Wall 3 | 4 | TARGETS = keygen showkey getstable getinsert getdelete statkey getscan get20-80Insert 5 | # -------------------------------------------------------------------------- 6 | all: ${TARGETS} 7 | 8 | keygen: keygen.c 9 | showkey: showkey.c 10 | 11 | getstable: getstable.c 12 | getinsert: getinsert.c 13 | get20-80Insert: get20-80Insert.c 14 | getdelete: getdelete.c 15 | getstart: getstart.c 16 | getscan: getscan.c 17 | 18 | statkey: statkey.c 19 | 20 | clean: 21 | -rm -f ${TARGETS} a.out *.o core 22 | -------------------------------------------------------------------------------- /keygen-8B/README: -------------------------------------------------------------------------------- 1 | 1. The easiest way to generate the keys is as follows: 2 | 3 | $ make 4 | $ ./mygen.sh 5 | 6 | 2. If you would like to change the file names and number of keys generated 7 | please have a look at mygen.sh. Each individual command also prints 8 | a usage message if invoked without any arguments. 9 | -------------------------------------------------------------------------------- /keygen-8B/get20-80Insert.c: -------------------------------------------------------------------------------- 1 | /* File Name: getinsert.c 2 | * Author: Shimin Chen 3 | * 4 | * Description: generate insertion keys 5 | * 6 | * getinsert 7 | * 8 | * randomly generate "insert_num" distinct output keys that are 9 | * not in the set of input keys. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "keygen.h" 21 | 22 | Int64 * get_keys (char *filename, Int64 num) 23 | {int fd; 24 | Int64 *p; 25 | 26 | fd = open (filename, 0, 0600); 27 | if (fd == -1) { 28 | perror ("open"); exit(1); 29 | } 30 | 31 | p = (Int64 *) malloc (num * sizeof(Int64)); 32 | if (p == NULL) { 33 | printf ("malloc error\n"); exit (1); 34 | } 35 | 36 | if (read (fd, p, num*sizeof(Int64)) != num * sizeof(Int64)) 37 | {perror ("read"); exit (1);} 38 | 39 | close (fd); 40 | return p; 41 | } 42 | 43 | static void write_once (char *name, void *buf, Int64 nbyte) 44 | {int fd; 45 | 46 | fd = creat (name, 0644); 47 | if (fd == -1) { 48 | perror (name); exit (1); 49 | } 50 | 51 | if (write (fd, buf, nbyte) != nbyte) { 52 | perror ("write"); exit (1); 53 | } 54 | 55 | close (fd); 56 | } 57 | 58 | int compare (const void * ip1, const void * ip2) 59 | { 60 | Int64 tt= (*(Int64 *)ip1 - *(Int64 *)ip2); 61 | return ((tt>0)?1: ((tt<0)?-1:0)); 62 | } 63 | 64 | static inline Int64 gen_a_key() 65 | { 66 | Int64 a= random(); 67 | Int64 b= random(); 68 | Int64 c= random(); 69 | return (((a<<32)^(b<<16)^(c)) & (0x7FFFFFFFFFFFFFFFLL)); 70 | } 71 | 72 | Int64 get_not_in_lower (Int64 keys[], Int64 keynum, Int64 threashold) 73 | {Int64 k; 74 | 75 | do{ 76 | k = gen_a_key(); 77 | k = k % threashold; 78 | }while(bsearch (&k, keys, keynum ,sizeof(Int64), compare)!=NULL); 79 | 80 | return k; 81 | } 82 | 83 | Int64 get_not_in_higher (Int64 keys[], Int64 keynum, Int64 threashold) 84 | {Int64 k; 85 | 86 | do{ 87 | k = gen_a_key(); 88 | } while((k < threashold) || 89 | (bsearch (&k, keys, keynum ,sizeof(Int64), compare)!=NULL)); 90 | 91 | return k; 92 | } 93 | 94 | int is_in (Int64 keys[], Int64 keynum, Int64 k) 95 | {Int64 i; 96 | for (i=0; i 0) 113 | printf ("%lld duplicates found\n", count); 114 | } while (count > 0); 115 | } 116 | 117 | void shuffle (Int64 from[], Int64 to[], Int64 keynum) 118 | {Int64 r, i, j; 119 | 120 | j=0; 121 | srand48(time(NULL)); 122 | for (i=keynum-1; i>=0; i--) { 123 | // r is uniformly distributed in 0,1,...,i 124 | r = drand48()*(i+1); 125 | to[j++] = from[r]; 126 | from[r] = from[i]; 127 | } 128 | } 129 | 130 | int main (int argc, char *argv[]) 131 | { 132 | Int64 keynum; 133 | char *input_keyfile; 134 | Int64 insertnum; 135 | char *output_file; 136 | 137 | Int64 *keys; 138 | Int64 *insertion; 139 | Int64 i, count; 140 | 141 | Int64 threshold, insertnum_first_80, count_lower; 142 | 143 | if (argc < 5) { 144 | fprintf (stderr, "Usage: %s \n", argv[0]); 145 | exit (1); 146 | } 147 | keynum = atoll (argv[1]); 148 | input_keyfile = argv[2]; 149 | insertnum = atoll (argv[3]); 150 | output_file = argv[4]; 151 | 152 | /* get input keys */ 153 | keys = get_keys (input_keyfile, keynum); 154 | threshold= keys[(int)(keynum / 5)]; 155 | 156 | /* allocate memory */ 157 | insertion = (Int64 *) malloc (insertnum * sizeof(Int64)); 158 | if (insertion == NULL) { 159 | printf ("no memory\n"); exit (1); 160 | } 161 | 162 | // initial insertion keys 163 | insertnum_first_80 = insertnum / 5 * 4; 164 | printf ("insertnum_first_80 = %lld\n", insertnum_first_80); 165 | 166 | for (i=0; i 0) 186 | printf ("%lld duplicates found\n", count); 187 | } while (count > 0); 188 | 189 | // shuffle the insertion keys 190 | shuffle(insertion, keys, insertnum); 191 | 192 | write_once (output_file, keys, insertnum*sizeof(Int64)); 193 | 194 | count_lower= 0; 195 | for (i=0; i 7 | * 8 | * generate "delete_num" of distinct keys that exist in the 9 | * input_keyfile. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "keygen.h" 21 | 22 | Int64 * get_keys (char *filename, Int64 num) 23 | {int fd; 24 | Int64 *p; 25 | 26 | fd = open (filename, 0, 0600); 27 | if (fd == -1) { 28 | perror ("open"); exit(1); 29 | } 30 | 31 | p = (Int64 *) malloc (num * sizeof(Int64)); 32 | if (p == NULL) { 33 | printf ("malloc error\n"); exit (1); 34 | } 35 | 36 | if (read (fd, p, num*sizeof(Int64)) != num * sizeof(Int64)) 37 | {perror ("read"); exit (1);} 38 | 39 | close (fd); 40 | return p; 41 | } 42 | 43 | static void write_once (char *name, void *buf, Int64 nbyte) 44 | {int fd; 45 | 46 | fd = creat (name, 0644); 47 | if (fd == -1) { 48 | perror (name); exit (1); 49 | } 50 | 51 | if (write (fd, buf, nbyte) != nbyte) { 52 | perror ("write"); exit (1); 53 | } 54 | 55 | close (fd); 56 | } 57 | 58 | void shuffle (Int64 from[], Int64 to[], Int64 keynum) 59 | {Int64 r, i, j; 60 | 61 | j=0; 62 | srand48(time(NULL)); 63 | for (i=keynum-1; i>=0; i--) { 64 | // r is uniformly distributed in 0,1,...,i 65 | r = drand48()*(i+1); 66 | to[j++] = from[r]; 67 | from[r] = from[i]; 68 | } 69 | } 70 | 71 | // try to reuse the code of getstable.c as much as possible 72 | int main (int argc, char *argv[]) 73 | { 74 | Int64 keynum; 75 | char *input_keyfile; 76 | Int64 deletenum; 77 | char *output_file; 78 | 79 | Int64 *keys; 80 | Int64 *deletion; 81 | 82 | if (argc < 5) { 83 | fprintf (stderr, "Usage: %s \n", argv[0]); 84 | exit (1); 85 | } 86 | keynum = atoll (argv[1]); 87 | input_keyfile = argv[2]; 88 | deletenum = atoll (argv[3]); 89 | output_file = argv[4]; 90 | 91 | keys = get_keys (input_keyfile, keynum); 92 | deletion = (Int64 *) malloc (keynum * sizeof(Int64)); 93 | if (deletion == NULL) { 94 | printf ("no memory\n"); exit (1); 95 | } 96 | 97 | shuffle (keys, deletion, keynum); 98 | 99 | write_once (output_file, deletion, deletenum*sizeof(Int64)); 100 | 101 | // free memory 102 | free (deletion); free (keys); 103 | 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /keygen-8B/getinsert.c: -------------------------------------------------------------------------------- 1 | /* File Name: getinsert.c 2 | * Author: Shimin Chen 3 | * 4 | * Description: generate insertion keys 5 | * 6 | * getinsert 7 | * 8 | * randomly generate "insert_num" distinct output keys that are 9 | * not in the set of input keys. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "keygen.h" 21 | 22 | Int64 * get_keys (char *filename, Int64 num) 23 | {int fd; 24 | Int64 *p; 25 | 26 | fd = open (filename, 0, 0600); 27 | if (fd == -1) { 28 | perror ("open"); exit(1); 29 | } 30 | 31 | p = (Int64 *) malloc (num * sizeof(Int64)); 32 | if (p == NULL) { 33 | printf ("malloc error\n"); exit (1); 34 | } 35 | 36 | if (read (fd, p, num*sizeof(Int64)) != num * sizeof(Int64)) 37 | {perror ("read"); exit (1);} 38 | 39 | close (fd); 40 | return p; 41 | } 42 | 43 | static void write_once (char *name, void *buf, Int64 nbyte) 44 | {int fd; 45 | 46 | fd = creat (name, 0644); 47 | if (fd == -1) { 48 | perror (name); exit (1); 49 | } 50 | 51 | if (write (fd, buf, nbyte) != nbyte) { 52 | perror ("write"); exit (1); 53 | } 54 | 55 | close (fd); 56 | } 57 | 58 | int compare (const void * ip1, const void * ip2) 59 | { 60 | Int64 tt= (*(Int64 *)ip1 - *(Int64 *)ip2); 61 | return ((tt>0)?1: ((tt<0)?-1:0)); 62 | } 63 | 64 | static inline Int64 gen_a_key() 65 | { 66 | Int64 a= random(); 67 | Int64 b= random(); 68 | Int64 c= random(); 69 | return (((a<<32)^(b<<16)^(c)) & (0x7FFFFFFFFFFFFFFFLL)); 70 | } 71 | 72 | Int64 get_not_in (Int64 keys[], Int64 keynum) 73 | {Int64 k; 74 | 75 | do{ 76 | k = gen_a_key(); 77 | }while(bsearch (&k, keys, keynum ,sizeof(Int64), compare)!=NULL); 78 | 79 | return k; 80 | } 81 | 82 | int is_in (Int64 keys[], Int64 keynum, Int64 k) 83 | {Int64 i; 84 | for (i=0; i 0) 101 | printf ("%lld duplicates found\n", count); 102 | } while (count > 0); 103 | } 104 | 105 | void shuffle (Int64 from[], Int64 to[], Int64 keynum) 106 | {Int64 r, i, j; 107 | 108 | j=0; 109 | srand48(time(NULL)); 110 | for (i=keynum-1; i>=0; i--) { 111 | // r is uniformly distributed in 0,1,...,i 112 | r = drand48()*(i+1); 113 | to[j++] = from[r]; 114 | from[r] = from[i]; 115 | } 116 | } 117 | 118 | int main (int argc, char *argv[]) 119 | { 120 | Int64 keynum; 121 | char *input_keyfile; 122 | Int64 insertnum; 123 | char *output_file; 124 | 125 | Int64 *keys; 126 | Int64 *insertion; 127 | Int64 i, count; 128 | 129 | if (argc < 5) { 130 | fprintf (stderr, "Usage: %s \n", argv[0]); 131 | exit (1); 132 | } 133 | keynum = atoll (argv[1]); 134 | input_keyfile = argv[2]; 135 | insertnum = atoll (argv[3]); 136 | output_file = argv[4]; 137 | 138 | /* get input keys */ 139 | keys = get_keys (input_keyfile, keynum); 140 | 141 | /* allocate memory */ 142 | insertion = (Int64 *) malloc (insertnum * sizeof(Int64)); 143 | if (insertion == NULL) { 144 | printf ("no memory\n"); exit (1); 145 | } 146 | 147 | // initial insertion keys 148 | for (i=0; i 0) 162 | printf ("%lld duplicates found\n", count); 163 | } while (count > 0); 164 | 165 | // shuffle the insertion keys 166 | shuffle(insertion, keys, insertnum); 167 | 168 | write_once (output_file, keys, insertnum*sizeof(Int64)); 169 | 170 | /* free memory */ 171 | free (insertion); free (keys); 172 | 173 | return 0; 174 | } 175 | -------------------------------------------------------------------------------- /keygen-8B/getscan.c: -------------------------------------------------------------------------------- 1 | /* File Name: getscan.c 2 | * Author: Shimin Chen 3 | * 4 | * Description: generate keys for scan 5 | * 6 | * getscan 7 | * 8 | * The output_file contains the same set of keys in input_keyfile 9 | * but in different order. The first 10% of output keys are 10 | * randomly selected from input keys. Then they are sorted. 11 | * The rest 10% of output keys are the rest of the input keys 12 | * in random order. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "keygen.h" 24 | 25 | Int64 * get_keys (char *filename, Int64 num) 26 | {int fd; 27 | Int64 *p; 28 | 29 | fd = open (filename, 0, 0600); 30 | if (fd == -1) { 31 | perror ("open"); exit(1); 32 | } 33 | 34 | p = (Int64 *) malloc (num * sizeof(Int64)); 35 | if (p == NULL) { 36 | printf ("malloc error\n"); exit (1); 37 | } 38 | 39 | if (read (fd, p, num*sizeof(Int64)) != num * sizeof(Int64)) 40 | {perror ("read"); exit (1);} 41 | 42 | close (fd); 43 | return p; 44 | } 45 | 46 | static void write_once (char *name, void *buf, Int64 nbyte) 47 | {int fd; 48 | 49 | fd = creat (name, 0644); 50 | if (fd == -1) { 51 | perror (name); exit (1); 52 | } 53 | 54 | if (write (fd, buf, nbyte) != nbyte) { 55 | perror ("write"); exit (1); 56 | } 57 | 58 | close (fd); 59 | } 60 | 61 | void getScanKeys (Int64 keynum, Int64 from[], Int64 num_scan_keys, Int64 to[], Int64 distance) 62 | {Int64 r, i; 63 | 64 | srand48(time(NULL)); 65 | for (i=0; i \n", argv[0]); 83 | exit (1); 84 | } 85 | keynum = atoll(argv[1]); 86 | input_keyfile = argv[2]; 87 | num_scan_keys= atoll(argv[3]); 88 | distance= atoll(argv[4]); 89 | output_file = argv[5]; 90 | 91 | /* read keys and allocate memory */ 92 | keys = get_keys (input_keyfile, keynum); 93 | scan = (Int64 *) malloc (num_scan_keys * sizeof(Int64)); 94 | if (scan == NULL) { 95 | printf ("no memory\n"); exit (1); 96 | } 97 | 98 | /* get random keys */ 99 | getScanKeys (keynum, keys, num_scan_keys, scan, distance); 100 | 101 | write_once (output_file, scan, num_scan_keys*sizeof(Int64)); 102 | 103 | /* free memory */ 104 | free (scan); free (keys); 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /keygen-8B/getstable.c: -------------------------------------------------------------------------------- 1 | /* File Name: getstable.c 2 | * Author: Shimin Chen 3 | * 4 | * Description: generate keys for generating mature trees 5 | * 6 | * getstable 7 | * 8 | * The output_file contains the same set of keys in input_keyfile 9 | * but in different order. The first 10% of output keys are 10 | * randomly selected from input keys. Then they are sorted. 11 | * The rest 10% of output keys are the rest of the input keys 12 | * in random order. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "keygen.h" 24 | 25 | Int64 * get_keys (char *filename, Int64 num) 26 | {int fd; 27 | Int64 *p; 28 | 29 | fd = open (filename, 0, 0600); 30 | if (fd == -1) { 31 | perror ("open"); exit(1); 32 | } 33 | 34 | p = (Int64 *) malloc (num * sizeof(Int64)); 35 | if (p == NULL) { 36 | printf ("malloc error\n"); exit (1); 37 | } 38 | 39 | if (read (fd, p, num*sizeof(Int64)) != num * sizeof(Int64)) 40 | {perror ("read"); exit (1);} 41 | 42 | close (fd); 43 | return p; 44 | } 45 | 46 | static void write_once (char *name, void *buf, Int64 nbyte) 47 | {int fd; 48 | 49 | fd = creat (name, 0644); 50 | if (fd == -1) { 51 | perror (name); exit (1); 52 | } 53 | 54 | if (write (fd, buf, nbyte) != nbyte) { 55 | perror ("write"); exit (1); 56 | } 57 | 58 | close (fd); 59 | } 60 | 61 | void shuffle (Int64 from[], Int64 to[], Int64 keynum) 62 | {Int64 r, i, j; 63 | 64 | j=0; 65 | srand48(time(NULL)); 66 | for (i=keynum-1; i>=0; i--) { 67 | // r is uniformly distributed in 0,1,...,i 68 | r = drand48()*(i+1); 69 | to[j++] = from[r]; 70 | from[r] = from[i]; 71 | } 72 | } 73 | 74 | int compare (const void * ip1, const void * ip2) 75 | { 76 | Int64 tt= (*(Int64 *)ip1 - *(Int64 *)ip2); 77 | return ((tt>0)?1: ((tt<0)?-1:0)); 78 | } 79 | 80 | int main (int argc, char *argv[]) 81 | { 82 | Int64 keynum; 83 | char *input_keyfile, *output_file; 84 | Int64 *keys; 85 | Int64 *stable; 86 | Int64 i, n; 87 | 88 | /* get params */ 89 | if (argc < 4) { 90 | fprintf (stderr, "Usage: %s \n", argv[0]); 91 | exit (1); 92 | } 93 | keynum = atoll(argv[1]); 94 | input_keyfile = argv[2]; 95 | output_file = argv[3]; 96 | 97 | /* read keys and allocate memory */ 98 | keys = get_keys (input_keyfile, keynum); 99 | stable = (Int64 *) malloc (keynum * sizeof(Int64)); 100 | if (stable == NULL) { 101 | printf ("no memory\n"); exit (1); 102 | } 103 | 104 | /* get random keys */ 105 | shuffle (keys, stable, keynum); 106 | 107 | /* sort the first keynum/10 keys */ 108 | n = keynum/10; 109 | qsort (stable, n, sizeof(Int64), compare); 110 | for (i=0; i= stable[i+1]) { 112 | printf ("%lld error\n", i); 113 | exit (1); 114 | } 115 | 116 | write_once (output_file, stable, keynum*sizeof(Int64)); 117 | 118 | /* free memory */ 119 | free (stable); free (keys); 120 | 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /keygen-8B/keygen.c: -------------------------------------------------------------------------------- 1 | /* File Name: keygen.c 2 | * Author: Shimin Chen 3 | * 4 | * Description: generate keys 5 | * 6 | * - random keys (may have duplicates) 7 | * - sorted random keys (no duplicates) 8 | * - natural numbers 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "keygen.h" 20 | 21 | /* Generating keynum random numbers */ 22 | 23 | static inline Int64 gen_a_key() 24 | { 25 | Int64 a= random(); 26 | Int64 b= random(); 27 | Int64 c= random(); 28 | return (((a<<32)^(b<<16)^(c)) & (0x7FFFFFFFFFFFFFFFLL)); 29 | } 30 | 31 | void keygen (Int64 keynum, Int64 key[]) 32 | {Int64 i; 33 | srandom (time(NULL)); 34 | for (i=0; i0)?1: ((tt<0)?-1:0)); 47 | } 48 | 49 | void sortkey (Int64 keynum, Int64 key[]) 50 | {Int64 i, count; 51 | do { 52 | qsort (key, keynum, sizeof(Int64), compare); 53 | count = 0; 54 | for (i=0; i 0) 60 | printf ("%lld duplicates found\n", count); 61 | } while (count > 0); 62 | } 63 | 64 | /* main */ 65 | 66 | int main (int argc, char *argv[]) 67 | {Int64 keynum; 68 | char cmd; 69 | char *filename; 70 | 71 | int fd; 72 | Int64 *key, i; 73 | 74 | /* input param */ 75 | if (argc < 4) { 76 | fprintf (stderr, "Usage: %s \n", argv[0]); 77 | exit (1); 78 | } 79 | keynum = atoll (argv[1]); 80 | cmd = argv[2][0]; 81 | filename = argv[3]; 82 | 83 | /* allocate memory */ 84 | key = (Int64 *)malloc (sizeof(Int64) * keynum); 85 | if (key == NULL) { 86 | printf ("no memory\n"); exit (1); 87 | } 88 | 89 | /* generate keys */ 90 | if (cmd == 'n') { 91 | // generate natural numbers 92 | for (i=0; i 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "keygen.h" 15 | 16 | #define NUM_KEY_IN_BUFFER (1024*1024/sizeof(int)) 17 | 18 | Int64 key[NUM_KEY_IN_BUFFER]; 19 | 20 | int main (int argc, char *argv[]) 21 | { 22 | char *filename; 23 | 24 | int fd; 25 | int i, len, this_num; 26 | Int64 keynum; 27 | 28 | // get parameters 29 | if (argc < 2) { 30 | fprintf (stderr, "Usage: %s \n", argv[0]); 31 | exit (1); 32 | } 33 | filename = argv[1]; 34 | 35 | // open file for input 36 | fd = open (filename, 0, 0644); 37 | if (fd == -1) { 38 | perror ("open"); exit (1); 39 | } 40 | 41 | // read file contents and output 42 | keynum = 0; 43 | while ((len = read (fd, key, sizeof(key))) > 0) { 44 | assert (len % sizeof(Int64) == 0); 45 | this_num = len/sizeof(Int64); 46 | 47 | for (i=0; i 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "keygen.h" 18 | 19 | Int64 * get_keys (char *filename, Int64 num) 20 | {int fd; 21 | Int64 *p; 22 | 23 | fd = open (filename, 0, 0600); 24 | if (fd == -1) { 25 | perror ("open"); exit(1); 26 | } 27 | 28 | p = (Int64 *) malloc (num * sizeof(Int64)); 29 | if (p == NULL) { 30 | printf ("malloc error\n"); exit (1); 31 | } 32 | 33 | if (read (fd, p, num*sizeof(Int64)) != num * sizeof(Int64)) 34 | {perror ("read"); exit (1);} 35 | 36 | close (fd); 37 | return p; 38 | } 39 | 40 | // collect stats for [shift, shift+8) bits 41 | void get_stats (Int64 keynum, Int64 keys[], int shift) 42 | { 43 | Int64 count[256]; 44 | int ii; 45 | Int64 i; 46 | 47 | for(ii=0; ii<256; ii++) count[ii]= 0; 48 | 49 | for (i=0; i> shift) & 0x00ff); 51 | count[val] ++; 52 | } 53 | 54 | printf("shifting %d bits:\n", shift); 55 | printf("----------------------------------------------------------------------\n"); 56 | for(ii=0; ii<256; ii++) { 57 | printf("count[%02x]= %7.2lf%%\n", ii, (double)count[ii]/(double)keynum*100.0); 58 | } 59 | printf("\n\n"); 60 | fflush(stdout); 61 | } 62 | 63 | // try to reuse the code of getstable.c as much as possible 64 | int main (int argc, char *argv[]) 65 | { 66 | Int64 keynum; 67 | char *input_keyfile; 68 | Int64 *keys; 69 | int shift; 70 | 71 | if (argc < 3) { 72 | fprintf (stderr, "Usage: %s \n", argv[0]); 73 | exit (1); 74 | } 75 | keynum = atoll (argv[1]); 76 | input_keyfile = argv[2]; 77 | 78 | keys = get_keys (input_keyfile, keynum); 79 | 80 | for(shift=0; shift<64; shift+=8) { 81 | get_stats(keynum, keys, shift); 82 | } 83 | 84 | free (keys); 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /lbtree-src/lbtree.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * @file indirect-arronly.cc 3 | * @author Shimin Chen , Jihang Liu, Leying Chen 4 | * @version 1.0 5 | * 6 | * @section LICENSE 7 | * 8 | * TBD 9 | * 10 | * @section DESCRIPTION 11 | * 12 | * 13 | * The class implements a Btree with indirect arrays only. Each node contains 14 | * an indirect array. The design aims to have good search performance and 15 | * efficient solution for update consistency on NVM memory. However, the node 16 | * size is limited to up to 128B. 17 | */ 18 | 19 | #include "lbtree.h" 20 | 21 | /* ----------------------------------------------------------------- * 22 | useful structure 23 | * ----------------------------------------------------------------- */ 24 | static int last_slot_in_line[LEAF_KEY_NUM]; 25 | 26 | static void initUseful(void) 27 | { 28 | // line 0 29 | last_slot_in_line[0]= 2; 30 | last_slot_in_line[1]= 2; 31 | last_slot_in_line[2]= 2; 32 | 33 | // line 1 34 | last_slot_in_line[3]= 6; 35 | last_slot_in_line[4]= 6; 36 | last_slot_in_line[5]= 6; 37 | last_slot_in_line[6]= 6; 38 | 39 | // line 2 40 | last_slot_in_line[7]= 10; 41 | last_slot_in_line[8]= 10; 42 | last_slot_in_line[9]= 10; 43 | last_slot_in_line[10]=10; 44 | 45 | // line 3 46 | last_slot_in_line[11]=13; 47 | last_slot_in_line[12]=13; 48 | last_slot_in_line[13]=13; 49 | } 50 | 51 | /* ----------------------------------------------------------------- * 52 | bulk load 53 | * ----------------------------------------------------------------- */ 54 | 55 | /* generate a btree on the input keys. 56 | After generating the btree, level is returned. 57 | leaf and non-leaf nodes will be bfill full. 58 | bfill should be between 0 and 1. 59 | */ 60 | 61 | 62 | /** 63 | * build a subtree containing: input[start_key, start_key+num_key-1] 64 | * stop at target_level (leaf is at level 0) 65 | * 66 | * @param input keyInput instance 67 | * @param start_key keyInput index for the first key 68 | * @param num_key number of keys in the subtree 69 | * @param bfill filling factor in (0.0,1.0] 70 | * @param target_level stop buidling the subtree at target level 71 | * @param pfirst return pointers to the first node at each level 72 | * @param n_nodes return number of nodes at each level 73 | * 74 | * @retval the top level of the subtree (can be smaller than target_level 75 | * if the root level is smaller target_level) 76 | */ 77 | int lbtree::bulkloadSubtree( 78 | keyInput *input, int start_key, int num_key, 79 | float bfill, int target_level, 80 | Pointer8B pfirst[], int n_nodes[]) 81 | { 82 | // We assume that a tree cannot be higher than 32 levels 83 | int ncur[32]; // current node at every level 84 | int top_level; // top_level is the top level that this method builds 85 | 86 | assert(start_key>=0 && num_key > 0 && bfill>0.0 && bfill<=1.0 87 | && target_level>=0); 88 | 89 | 90 | // 1. compute leaf and nonleaf number of keys 91 | int leaf_fill_num= (int)((float)LEAF_KEY_NUM * bfill); 92 | leaf_fill_num= max(leaf_fill_num, 1); 93 | 94 | int nonleaf_fill_num= (int)((float)NON_LEAF_KEY_NUM * bfill); 95 | nonleaf_fill_num= max(nonleaf_fill_num, 1); 96 | 97 | 98 | // 2. compute number of nodes 99 | n_nodes[0]= ceiling(num_key, leaf_fill_num); 100 | top_level= 0; 101 | 102 | for (int i=1; n_nodes[i-1]>1 && i<=target_level; i++) { 103 | n_nodes[i]= ceiling(n_nodes[i-1], nonleaf_fill_num+1); 104 | top_level= i; 105 | } // end of for each nonleaf level 106 | 107 | 108 | // 3. allocate nodes 109 | pfirst[0]= nvmpool_alloc(sizeof(bleaf) * n_nodes[0]); 110 | for (int i=1; i<=top_level; i++) { 111 | pfirst[i]= mempool_alloc(sizeof(bnode) * n_nodes[i]); 112 | } 113 | // nvmpool_alloc/mempool_alloc call exit if out of memory 114 | 115 | 116 | // 4. populate nodes 117 | for (int ll=1; ll<=top_level; ll++) { 118 | ncur[ll]= 0; 119 | bnode *np= (bnode *)(pfirst[ll]); 120 | np->lock()= 0; np->num()= -1; 121 | } 122 | 123 | bleaf * leaf= pfirst[0]; 124 | int nodenum= n_nodes[0]; 125 | 126 | bleafMeta leaf_meta; 127 | leaf_meta.v.bitmap= ( ((1<=1 && fillnum<=leaf_fill_num); 141 | 142 | leaf_meta.v.bitmap= ( ((1<get_key(key_id)); 151 | key_id ++; 152 | 153 | // entry 154 | lp->k(j) = mykey; 155 | lp->ch(j) = (void *)mykey; 156 | 157 | // hash 158 | leaf_meta.v.fgpt[j]= hashcode1B(mykey); 159 | 160 | } // for each key in this leaf node 161 | 162 | // sibling pointer 163 | lp->next[0]= ((inext[1]= NULL; 165 | 166 | // 2x8B meta 167 | lp->setBothWords(&leaf_meta); 168 | 169 | 170 | // populate nonleaf node 171 | Pointer8B child= lp; 172 | key_type left_key= lp->k(LEAF_KEY_NUM-fillnum); 173 | 174 | // append (left_key, child) to level ll node 175 | // child is the level ll-1 node to be appended. 176 | // left_key is the left-most key in the subtree of child. 177 | for (int ll=1; ll<=top_level; ll++) { 178 | bnode *np= ((bnode *)(pfirst[ll])) + ncur[ll]; 179 | 180 | // if the node has >=1 child 181 | if (np->num() >= 0) { 182 | int kk= np->num()+1; 183 | np->ch(kk)= child; np->k(kk)= left_key; 184 | np->num()= kk; 185 | 186 | if ((kk==nonleaf_fill_num)&&(ncur[ll]lock()= 0; np->num()= -1; 189 | } 190 | break; 191 | } 192 | 193 | // new node 194 | np->ch(0)= child; np->num() = 0; 195 | 196 | // append this new node to parent 197 | child= np; 198 | 199 | } // for each nonleaf level 200 | 201 | } // end of foreach leaf node 202 | 203 | // 5. return 204 | return top_level; 205 | } 206 | 207 | /** 208 | * build a top tree containing: ptrs/keys[0..num_key-1] 209 | * stop at target_level 210 | * 211 | * @param ptrs child node pointers 212 | * @param keys left keys of subtrees rooted at child nodes 213 | * @param num_key number of child nodes 214 | * @param bfill filling factor in (0.0,1.0] 215 | * @param cur_level level of the child nodes 216 | * @param target_level stop buidling the subtree at target level 217 | * @param pfirst return pointers to the first node at each level 218 | * @param n_nodes return number of nodes at each level 219 | * 220 | * @retval the top level of the subtree (can be smaller than target_level 221 | * if the root level is smaller target_level) 222 | */ 223 | int lbtree::bulkloadToptree( 224 | Pointer8B ptrs[], key_type keys[], int num_key, 225 | float bfill, int cur_level, int target_level, 226 | Pointer8B pfirst[], int n_nodes[]) 227 | { 228 | // We assume that a tree cannot be higher than 32 levels 229 | int ncur[32]; // current node at every level 230 | int top_level; // top_level is the top level that this method builds 231 | 232 | assert(num_key >= 2 && bfill>0.0 && bfill<=1.0 233 | && cur_level >=0 && target_level>cur_level); 234 | 235 | // 1. compute nonleaf number of keys 236 | int nonleaf_fill_num= (int)((float)NON_LEAF_KEY_NUM * bfill); 237 | nonleaf_fill_num= max(nonleaf_fill_num, 1); 238 | 239 | 240 | // 2. compute number of nodes 241 | n_nodes[cur_level]= num_key; 242 | top_level= cur_level; 243 | 244 | for (int i=cur_level+1; n_nodes[i-1]>1 && i<=target_level; i++) { 245 | n_nodes[i]= ceiling(n_nodes[i-1], nonleaf_fill_num+1); 246 | top_level= i; 247 | } // end of for each nonleaf level 248 | 249 | 250 | // 3. allocate nodes 251 | for (int i=cur_level+1; i<=top_level; i++) { 252 | pfirst[i]= mempool_alloc(sizeof(bnode) * n_nodes[i]); 253 | } 254 | // mempool_alloc call exit if out of memory 255 | 256 | 257 | // 4. populate nodes 258 | for (int ll=cur_level+1; ll<=top_level; ll++) { 259 | ncur[ll]= 0; 260 | bnode *np= (bnode *)(pfirst[ll]); 261 | np->lock()= 0; np->num()= -1; 262 | } 263 | 264 | for (int i=0; i=1 child 275 | if (np->num() >= 0) { 276 | int kk= np->num()+1; 277 | np->ch(kk)= child; np->k(kk)= left_key; 278 | np->num()= kk; 279 | 280 | if ((kk==nonleaf_fill_num)&&(ncur[ll]lock()= 0; np->num()= -1; 283 | } 284 | break; 285 | } 286 | 287 | // new node 288 | np->ch(0)= child; np->num() = 0; 289 | 290 | // append this new node to parent 291 | child= np; 292 | 293 | } // for each nonleaf level 294 | 295 | } // end of foreach key 296 | 297 | // 5. return 298 | return top_level; 299 | } 300 | 301 | /** 302 | * Obtain the node pointers and left keys for a given level 303 | * 304 | * @param pnode subtree root 305 | * @param pnode_level subtree root level 306 | * @param left_key left key of the subtree 307 | * @param target_level the level to get nodes and keys 308 | * @param ptrs child node pointers (output) 309 | * @param keys left keys of subtrees rooted at child nodes (output) 310 | * @param num_nodes number of child nodes (output) 311 | * @param free_above_level_nodes if nodes above target_level should be freed 312 | * 313 | */ 314 | void lbtree::getKeyPtrLevel( 315 | Pointer8B pnode, int pnode_level, key_type left_key, 316 | int target_level, Pointer8B ptrs[], key_type keys[], int &num_nodes, 317 | bool free_above_level_nodes) 318 | { 319 | // already at this target_level 320 | if (pnode_level == target_level) { 321 | ptrs[num_nodes]= pnode; 322 | keys[num_nodes]= left_key; 323 | num_nodes ++; 324 | return; 325 | } 326 | 327 | // pnode_level > target_level 328 | else if (pnode_level > target_level) { 329 | bnode *p= pnode; 330 | getKeyPtrLevel(p->ch(0), pnode_level-1, left_key, 331 | target_level, ptrs, keys, num_nodes, 332 | free_above_level_nodes); 333 | for (int i=1; i<=p->num(); i++) { 334 | getKeyPtrLevel(p->ch(i), pnode_level-1, p->k(i), 335 | target_level, ptrs, keys, num_nodes, 336 | free_above_level_nodes); 337 | } 338 | 339 | if (free_above_level_nodes) mempool_free_node(p); 340 | } 341 | } 342 | 343 | 344 | typedef struct BldThArgs { 345 | 346 | key_type start_key; // input 347 | key_type num_key; // input 348 | 349 | int top_level; // output 350 | int n_nodes[32]; // output 351 | Pointer8B pfirst[32]; // output 352 | 353 | } BldThArgs; 354 | 355 | // 356 | // bulkload using multiple threads 357 | // 358 | int lbtree::bulkload (int keynum, keyInput *input, float bfill) 359 | { 360 | // 1. allocate BldThArgs[] 361 | int num_threads= ((keynum>worker_thread_num*10) ? worker_thread_num : 1); 362 | 363 | BldThArgs *bta= new BldThArgs[num_threads]; 364 | if (!bta) {perror("malloc"); exit(1);} 365 | 366 | 367 | // 2. one thread? 368 | if (num_threads == 1) { 369 | bta[0].top_level= bulkloadSubtree( 370 | input, 0, keynum, bfill, 31, 371 | bta[0].pfirst, bta[0].n_nodes); 372 | tree_meta->root_level= bta[0].top_level; 373 | tree_meta->tree_root= bta[0].pfirst[tree_meta->root_level]; 374 | tree_meta->setFirstLeaf(bta[0].pfirst[0]); 375 | 376 | // if this assertion is false, then the tree has > 31 levels 377 | assert(bta[0].n_nodes[bta[0].top_level] == 1); 378 | 379 | delete[] bta; 380 | return tree_meta->root_level; 381 | } 382 | 383 | 384 | // 3. compute start num_key for each thread 385 | int kn_per_thread= floor(keynum, num_threads); 386 | int kn_max= keynum-(num_threads-1)*kn_per_thread; 387 | 388 | for (int i=0; i= kn_per_thread) 395 | 396 | 397 | // 4. create threads to bulkload subtrees 398 | std::thread threads[num_threads]; 399 | for (int i=0; iopenCursor( 403 | bta[i].start_key, bta[i].num_key); 404 | bta[i].top_level= bulkloadSubtree( 405 | cursor, bta[i].start_key, bta[i].num_key, 406 | bfill, 31, 407 | bta[i].pfirst, bta[i].n_nodes); 408 | input->closeCursor(cursor); 409 | }); 410 | } 411 | for (int i=0; inext[0]= bta[i].pfirst[0]; 417 | } 418 | 419 | 420 | // 5. put the ptr to the top nonleaf nodes into an array 421 | // using the min top level 422 | int level= bta[0].top_level; // subtree 0 .. num_threads-2 423 | 424 | Pointer8B top_ptrs[num_threads*3]; // should be < 2*num_threads 425 | key_type top_keys[num_threads*3]; 426 | int num_nodes= 0; 427 | 428 | for (int i=0; ik(LEAF_KEY_NUM - lp->num()); 431 | getKeyPtrLevel(bta[i].pfirst[bta[i].top_level], 432 | bta[i].top_level, left_key, 433 | level, top_ptrs, top_keys, num_nodes, true); 434 | } 435 | 436 | // Otherwise, top_keys[] and top_ptrs[] are not large enough (can't be true) 437 | assert(num_nodes <= sizeof(top_keys)/sizeof(key_type)); 438 | 439 | 440 | // 6. build the top nonleaf nodes 441 | bta[0].top_level= bulkloadToptree(top_ptrs, top_keys, num_nodes, bfill, 442 | level, 31, bta[0].pfirst, bta[0].n_nodes); 443 | 444 | tree_meta->root_level= bta[0].top_level; 445 | tree_meta->tree_root= bta[0].pfirst[tree_meta->root_level]; 446 | tree_meta->setFirstLeaf(bta[0].pfirst[0]); 447 | 448 | // if this assertion is false, then the tree has > 31 levels 449 | assert(bta[0].n_nodes[bta[0].top_level] == 1); 450 | 451 | // 7. free BldThArgs[] 452 | delete[] bta; 453 | 454 | return tree_meta->root_level; 455 | } 456 | 457 | 458 | /* ----------------------------------------------------------------- * 459 | look up 460 | * ----------------------------------------------------------------- */ 461 | 462 | /* leaf is level 0, root is level depth-1 */ 463 | 464 | void * lbtree::lookup (key_type key, int *pos) 465 | { 466 | bnode *p; 467 | bleaf *lp; 468 | int i,t,m,b; 469 | key_type r; 470 | 471 | unsigned char key_hash= hashcode1B(key); 472 | int ret_pos; 473 | 474 | Again1: 475 | // 1. RTM begin 476 | if(_xbegin() != _XBEGIN_STARTED) goto Again1; 477 | 478 | // 2. search nonleaf nodes 479 | p = tree_meta->tree_root; 480 | 481 | for (i=tree_meta->root_level; i>0; i--) { 482 | 483 | // prefetch the entire node 484 | NODE_PREF(p); 485 | 486 | // if the lock bit is set, abort 487 | if (p->lock()) {_xabort(1); goto Again1;} 488 | 489 | // binary search to narrow down to at most 8 entries 490 | b=1; t=p->num(); 491 | while (b+7<=t) { 492 | m=(b+t) >>1; 493 | r= key - p->k(m); 494 | if (r>0) b=m+1; 495 | else if (r<0) t = m-1; 496 | else {p=p->ch(m); goto inner_done;} 497 | } 498 | 499 | // sequential search (which is slightly faster now) 500 | for (; b<=t; b++) 501 | if (key < p->k(b)) break; 502 | p = p->ch(b-1); 503 | 504 | inner_done: ; 505 | } 506 | 507 | // 3. search leaf node 508 | lp= (bleaf *)p; 509 | 510 | // prefetch the entire node 511 | LEAF_PREF (lp); 512 | 513 | // if the lock bit is set, abort 514 | if (lp->lock) {_xabort(2); goto Again1;} 515 | 516 | // SIMD comparison 517 | // a. set every byte to key_hash in a 16B register 518 | __m128i key_16B = _mm_set1_epi8((char)key_hash); 519 | 520 | // b. load meta into another 16B register 521 | __m128i fgpt_16B= _mm_load_si128((const __m128i*)lp); 522 | 523 | // c. compare them 524 | __m128i cmp_res = _mm_cmpeq_epi8(key_16B, fgpt_16B); 525 | 526 | // d. generate a mask 527 | unsigned int mask= (unsigned int) 528 | _mm_movemask_epi8(cmp_res); // 1: same; 0: diff 529 | 530 | // remove the lower 2 bits then AND bitmap 531 | mask= (mask >> 2)&((unsigned int)(lp->bitmap)); 532 | 533 | // search every matching candidate 534 | ret_pos= -1; 535 | while (mask) { 536 | int jj = bitScan(mask)-1; // next candidate 537 | 538 | if (lp->k(jj) == key) { // found 539 | ret_pos= jj; 540 | break; 541 | } 542 | 543 | mask &= ~(0x1<= end) return; 562 | 563 | int pos_start= pos[start]; 564 | key_type key= p->k(pos_start); // pivot 565 | int l, r; 566 | 567 | l= start; r=end; 568 | while (lk(pos[r])>key)) r--; 570 | if (lk(pos[l])<=key)) l++; 575 | if (l0; i--) sum += i; 616 | goto Again2; 617 | } 618 | 619 | // 2. search nonleaf nodes 620 | p = tree_meta->tree_root; 621 | 622 | for (i=tree_meta->root_level; i>0; i--) { 623 | 624 | // prefetch the entire node 625 | NODE_PREF(p); 626 | 627 | // if the lock bit is set, abort 628 | if (p->lock()) {_xabort(3); goto Again2;} 629 | 630 | parray[i]= p; 631 | isfull[i]= (p->num() == NON_LEAF_KEY_NUM); 632 | 633 | // binary search to narrow down to at most 8 entries 634 | b=1; t=p->num(); 635 | while (b+7<=t) { 636 | m=(b+t) >>1; 637 | r= key - p->k(m); 638 | if (r>0) b=m+1; 639 | else if (r<0) t = m-1; 640 | else {p=p->ch(m); ppos[i]=m; goto inner_done;} 641 | } 642 | 643 | // sequential search (which is slightly faster now) 644 | for (; b<=t; b++) 645 | if (key < p->k(b)) break; 646 | p = p->ch(b-1); ppos[i]= b-1; 647 | 648 | inner_done: ; 649 | } 650 | 651 | // 3. search leaf node 652 | lp= (bleaf *)p; 653 | 654 | // prefetch the entire node 655 | LEAF_PREF (lp); 656 | 657 | // if the lock bit is set, abort 658 | if (lp->lock) {_xabort(4); goto Again2;} 659 | 660 | parray[0]= lp; 661 | 662 | // SIMD comparison 663 | // a. set every byte to key_hash in a 16B register 664 | __m128i key_16B = _mm_set1_epi8((char)key_hash); 665 | 666 | // b. load meta into another 16B register 667 | __m128i fgpt_16B= _mm_load_si128((const __m128i*)lp); 668 | 669 | // c. compare them 670 | __m128i cmp_res = _mm_cmpeq_epi8(key_16B, fgpt_16B); 671 | 672 | // d. generate a mask 673 | unsigned int mask= (unsigned int) 674 | _mm_movemask_epi8(cmp_res); // 1: same; 0: diff 675 | 676 | // remove the lower 2 bits then AND bitmap 677 | mask= (mask >> 2)&((unsigned int)(lp->bitmap)); 678 | 679 | // search every matching candidate 680 | while (mask) { 681 | int jj = bitScan(mask)-1; // next candidate 682 | 683 | if (lp->k(jj) == key) { // found: do nothing, return 684 | _xend(); 685 | return; 686 | } 687 | 688 | mask &= ~(0x1<lock= 1; 693 | 694 | isfull[0]= lp->isFull(); 695 | if (isfull[0]) { 696 | for (i=1; i<=tree_meta->root_level; i++) { 697 | p= parray[i]; 698 | p->lock()= 1; 699 | if (! isfull[i]) break; 700 | } 701 | } 702 | 703 | // 5. RTM commit 704 | _xend(); 705 | 706 | } // end of Part 1 707 | 708 | /* Part 2. leaf node */ 709 | { 710 | bleaf *lp= parray[0]; 711 | bleafMeta meta= *((bleafMeta *)lp); 712 | 713 | 714 | /* 1. leaf is not full */ 715 | if (! isfull[0]) { 716 | 717 | meta.v.lock= 0; // clear lock in temp meta 718 | 719 | // 1.1 get first empty slot 720 | uint16_t bitmap= meta.v.bitmap; 721 | int slot= bitScan(~bitmap)-1; 722 | 723 | // 1.2 set leaf.entry[slot]= (k, v); 724 | // set fgpt, bitmap in meta 725 | lp->k(slot)= key; 726 | lp->ch(slot)= ptr; 727 | meta.v.fgpt[slot]= key_hash; 728 | bitmap |= (1<setWord0(&meta); 736 | 737 | // 1.3.2 flush 738 | clwb(lp); sfence(); 739 | 740 | return; 741 | } 742 | 743 | // 1.4 line 1--3 744 | else { 745 | int last_slot= last_slot_in_line[slot]; 746 | int from= 0; 747 | for (int to=slot+1; to<=last_slot; to++) { 748 | if ((bitmap&(1<ent[to]= lp->ent[from]; 752 | meta.v.fgpt[to]= meta.v.fgpt[from]; 753 | bitmap |= (1<k(slot))); sfence(); 760 | 761 | // 1.4.3 change meta and flush line 0 762 | meta.v.bitmap= bitmap; 763 | lp->setBothWords(&meta); 764 | clwb(lp); sfence(); 765 | 766 | return; 767 | } 768 | } // end of not full 769 | 770 | /* 2. leaf is full, split */ 771 | 772 | // 2.1 get sorted positions 773 | int sorted_pos[LEAF_KEY_NUM]; 774 | for (int i=0; ik(sorted_pos[split]); 780 | 781 | // 2.3 create new node 782 | bleaf * newp = (bleaf *)nvmpool_alloc_node(LEAF_SIZE); 783 | 784 | // 2.4 move entries sorted_pos[split .. LEAF_KEY_NUM-1] 785 | uint16_t freed_slots= 0; 786 | for (int i=split; ient[i]= lp->ent[sorted_pos[i]]; 788 | newp->fgpt[i]= lp->fgpt[sorted_pos[i]]; 789 | 790 | // add to freed slots bitmap 791 | freed_slots |= (1<bitmap= (((1<<(LEAF_KEY_NUM - split))-1) << split); 794 | newp->lock= 0; newp->alt= 0; 795 | 796 | // remove freed slots from temp bitmap 797 | meta.v.bitmap &= ~freed_slots; 798 | 799 | newp->next[0]= lp->next[lp->alt]; 800 | lp->next[1-lp->alt]= newp; 801 | 802 | // set alt in temp bitmap 803 | meta.v.alt= 1 - lp->alt; 804 | 805 | // 2.5 key > split_key: insert key into new node 806 | if (key > split_key) { 807 | newp->k(split-1)= key; newp->ch(split-1)= ptr; 808 | newp->fgpt[split-1]= key_hash; 809 | newp->bitmap |= 1<<(split-1); 810 | 811 | if (tree_meta->root_level > 0) meta.v.lock= 0; // do not clear lock of root 812 | } 813 | 814 | // 2.6 clwb newp, clwb lp line[3] and sfence 815 | LOOP_FLUSH(clwb, newp, LEAF_LINE_NUM); 816 | clwb(&(lp->next[0])); 817 | sfence(); 818 | 819 | // 2.7 clwb lp and flush: NVM atomic write to switch alt and set bitmap 820 | lp->setBothWords(&meta); 821 | clwb(lp); sfence(); 822 | 823 | // 2.8 key < split_key: insert key into old node 824 | if (key <= split_key) { 825 | 826 | // note: lock bit is still set 827 | if (tree_meta->root_level > 0) meta.v.lock= 0; // do not clear lock of root 828 | 829 | // get first empty slot 830 | uint16_t bitmap= meta.v.bitmap; 831 | int slot= bitScan(~bitmap)-1; 832 | 833 | // set leaf.entry[slot]= (k, v); 834 | // set fgpt, bitmap in meta 835 | lp->k(slot)= key; 836 | lp->ch(slot)= ptr; 837 | meta.v.fgpt[slot]= key_hash; 838 | bitmap |= (1<setWord0(&meta); 846 | // flush 847 | clwb(lp); sfence(); 848 | } 849 | // line 1--3 850 | else { 851 | int last_slot= last_slot_in_line[slot]; 852 | int from= 0; 853 | for (int to=slot+1; to<=last_slot; to++) { 854 | if ((bitmap&(1<ent[to]= lp->ent[from]; 858 | meta.v.fgpt[to]= meta.v.fgpt[from]; 859 | bitmap |= (1<k(slot))); sfence(); 866 | 867 | // change meta and flush line 0 868 | meta.v.bitmap= bitmap; 869 | lp->setBothWords(&meta); 870 | clwb(lp); sfence(); 871 | } 872 | } 873 | 874 | key= split_key; ptr= newp; 875 | /* (key, ptr) to be inserted in the parent non-leaf */ 876 | 877 | } // end of Part 2 878 | 879 | /* Part 3. nonleaf node */ 880 | {bnode *p, *newp; 881 | int n, i, pos, r, lev, total_level; 882 | 883 | #define LEFT_KEY_NUM ((NON_LEAF_KEY_NUM)/2) 884 | #define RIGHT_KEY_NUM ((NON_LEAF_KEY_NUM) - LEFT_KEY_NUM) 885 | 886 | total_level = tree_meta->root_level; 887 | lev = 1; 888 | 889 | while (lev <= total_level) { 890 | 891 | p = parray[lev]; 892 | n = p->num(); 893 | pos = ppos[lev] + 1; // the new child is ppos[lev]+1 >= 1 894 | 895 | /* if the non-leaf is not full, simply insert key ptr */ 896 | 897 | if (n < NON_LEAF_KEY_NUM) { 898 | for (i=n; i>=pos; i--) p->ent[i+1]= p->ent[i]; 899 | 900 | p->k(pos) = key; p->ch(pos) = ptr; 901 | p->num() = n+1; 902 | sfence(); 903 | 904 | // unlock after all changes are globally visible 905 | p->lock()= 0; 906 | return; 907 | } 908 | 909 | /* otherwise allocate a new non-leaf and redistribute the keys */ 910 | newp = (bnode *)mempool_alloc_node(NONLEAF_SIZE); 911 | 912 | /* if key should be in the left node */ 913 | if (pos <= LEFT_KEY_NUM) { 914 | for (r=RIGHT_KEY_NUM, i=NON_LEAF_KEY_NUM; r>=0; r--, i--) { 915 | newp->ent[r]= p->ent[i]; 916 | } 917 | /* newp->key[0] actually is the key to be pushed up !!! */ 918 | for (i=LEFT_KEY_NUM-1; i>=pos; i--) p->ent[i+1]= p->ent[i]; 919 | 920 | p->k(pos) = key; p->ch(pos) = ptr; 921 | } 922 | /* if key should be in the right node */ 923 | else { 924 | for (r=RIGHT_KEY_NUM, i=NON_LEAF_KEY_NUM; i>=pos; i--, r--){ 925 | newp->ent[r]= p->ent[i]; 926 | } 927 | newp->k(r) = key; newp->ch(r) = ptr; r--; 928 | for (;r>=0; r--, i--) { 929 | newp->ent[r]= p->ent[i]; 930 | } 931 | } /* end of else */ 932 | 933 | key = newp->k(0); ptr = newp; 934 | 935 | p->num() = LEFT_KEY_NUM; 936 | if (lev < total_level) p->lock()= 0; // do not clear lock bit of root 937 | newp->num() = RIGHT_KEY_NUM; newp->lock()= 0; 938 | 939 | lev ++; 940 | } /* end of while loop */ 941 | 942 | /* root was splitted !! add another level */ 943 | newp = (bnode *)mempool_alloc_node(NONLEAF_SIZE); 944 | 945 | newp->num() = 1; newp->lock()= 1; 946 | newp->ch(0) = tree_meta->tree_root; newp->ch(1) = ptr; newp->k(1) = key; 947 | sfence(); // ensure new node is consistent 948 | 949 | void *old_root= tree_meta->tree_root; 950 | tree_meta->root_level = lev; 951 | tree_meta->tree_root = newp; 952 | sfence(); // tree root change is globablly visible 953 | // old root and new root are both locked 954 | 955 | // unlock old root 956 | if (total_level > 0) { // previous root is a nonleaf 957 | ((bnode *)old_root)->lock()= 0; 958 | } 959 | else { // previous root is a leaf 960 | ((bleaf *)old_root)->lock= 0; 961 | } 962 | 963 | // unlock new root 964 | newp->lock()= 0; 965 | 966 | return; 967 | 968 | #undef RIGHT_KEY_NUM 969 | #undef LEFT_KEY_NUM 970 | } 971 | } 972 | 973 | /* ---------------------------------------------------------- * 974 | 975 | deletion 976 | 977 | lazy delete - insertions >= deletions in most cases 978 | so no need to change the tree structure frequently 979 | 980 | So unless there is no key in a leaf or no child in a non-leaf, 981 | the leaf and non-leaf won't be deleted. 982 | 983 | * ---------------------------------------------------------- */ 984 | void lbtree::del (key_type key) 985 | { 986 | // record the path from root to leaf 987 | // parray[level] is a node on the path 988 | // child ppos[level] of parray[level] == parray[level-1] 989 | // 990 | Pointer8B parray[32]; // 0 .. root_level will be used 991 | short ppos[32]; // 0 .. root_level will be used 992 | bleaf * leaf_sibp= NULL; // left sibling of the target leaf 993 | 994 | unsigned char key_hash= hashcode1B(key); 995 | volatile long long sum; 996 | 997 | /* Part 1. get the positions to insert the key */ 998 | { bnode *p; 999 | bleaf *lp; 1000 | int i,t,m,b; 1001 | key_type r; 1002 | 1003 | Again3: 1004 | // 1. RTM begin 1005 | if(_xbegin() != _XBEGIN_STARTED) { 1006 | // random backoff 1007 | // sum= 0; 1008 | // for (int i=(rdtsc() % 1024); i>0; i--) sum += i; 1009 | goto Again3; 1010 | } 1011 | 1012 | // 2. search nonleaf nodes 1013 | p = tree_meta->tree_root; 1014 | 1015 | for (i=tree_meta->root_level; i>0; i--) { 1016 | 1017 | // prefetch the entire node 1018 | NODE_PREF(p); 1019 | 1020 | // if the lock bit is set, abort 1021 | if (p->lock()) {_xabort(5); goto Again3;} 1022 | 1023 | parray[i]= p; 1024 | 1025 | // binary search to narrow down to at most 8 entries 1026 | b=1; t=p->num(); 1027 | while (b+7<=t) { 1028 | m=(b+t) >>1; 1029 | r= key - p->k(m); 1030 | if (r>0) b=m+1; 1031 | else if (r<0) t = m-1; 1032 | else {p=p->ch(m); ppos[i]=m; goto inner_done;} 1033 | } 1034 | 1035 | // sequential search (which is slightly faster now) 1036 | for (; b<=t; b++) 1037 | if (key < p->k(b)) break; 1038 | p = p->ch(b-1); ppos[i]= b-1; 1039 | 1040 | inner_done: ; 1041 | } 1042 | 1043 | // 3. search leaf node 1044 | lp= (bleaf *)p; 1045 | 1046 | // prefetch the entire node 1047 | LEAF_PREF (lp); 1048 | 1049 | // if the lock bit is set, abort 1050 | if (lp->lock) {_xabort(6); goto Again3;} 1051 | 1052 | parray[0]= lp; 1053 | 1054 | // SIMD comparison 1055 | // a. set every byte to key_hash in a 16B register 1056 | __m128i key_16B = _mm_set1_epi8((char)key_hash); 1057 | 1058 | // b. load meta into another 16B register 1059 | __m128i fgpt_16B= _mm_load_si128((const __m128i*)lp); 1060 | 1061 | // c. compare them 1062 | __m128i cmp_res = _mm_cmpeq_epi8(key_16B, fgpt_16B); 1063 | 1064 | // d. generate a mask 1065 | unsigned int mask= (unsigned int) 1066 | _mm_movemask_epi8(cmp_res); // 1: same; 0: diff 1067 | 1068 | // remove the lower 2 bits then AND bitmap 1069 | mask= (mask >> 2)&((unsigned int)(lp->bitmap)); 1070 | 1071 | // search every matching candidate 1072 | i= -1; 1073 | while (mask) { 1074 | int jj = bitScan(mask)-1; // next candidate 1075 | 1076 | if (lp->k(jj) == key) { // found: good 1077 | i= jj; 1078 | break; 1079 | } 1080 | 1081 | mask &= ~(0x1<lock= 1; 1093 | 1094 | if (lp->num() == 1) { 1095 | 1096 | // look for its left sibling 1097 | for (i=1; i<=tree_meta->root_level; i++) { 1098 | if (ppos[i]>=1) break; 1099 | } 1100 | 1101 | if (i <= tree_meta->root_level) { 1102 | p= parray[i]; 1103 | p= p->ch(ppos[i]-1); i--; 1104 | 1105 | for (; i>=1; i--) { 1106 | p= p->ch(p->num()); 1107 | } 1108 | 1109 | leaf_sibp= (bleaf *)p; 1110 | if (leaf_sibp->lock) {_xabort(7); goto Again3;} 1111 | 1112 | // lock leaf_sibp 1113 | leaf_sibp->lock= 1; 1114 | } 1115 | 1116 | // lock affected ancestors 1117 | for (i=1; i<=tree_meta->root_level; i++) { 1118 | p= (bnode *) parray[i]; 1119 | p->lock()= 1; 1120 | 1121 | if (p->num() >= 1) break; // at least 2 children, ok to stop 1122 | } 1123 | } 1124 | 1125 | // 5. RTM commit 1126 | _xend(); 1127 | 1128 | } // end of Part 1 1129 | 1130 | /* Part 2. leaf node */ 1131 | { 1132 | bleaf *lp= parray[0]; 1133 | 1134 | /* 1. leaf contains more than one key */ 1135 | /* If this leaf node is the root, we cannot delete the root. */ 1136 | if ((lp->num()>1)||(tree_meta->root_level==0)) { 1137 | bleafMeta meta= *((bleafMeta *)lp); 1138 | 1139 | meta.v.lock= 0; // clear lock in temp meta 1140 | meta.v.bitmap &= ~(1<setWord0(&meta); 1142 | clwb(lp); sfence(); 1143 | 1144 | return; 1145 | 1146 | } // end of more than one key 1147 | 1148 | /* 2. leaf has only one key: remove the leaf node */ 1149 | 1150 | /* if it has a left sibling */ 1151 | if (leaf_sibp != NULL) { 1152 | // remove it from sibling linked list 1153 | leaf_sibp->next[leaf_sibp->alt]= lp->next[lp->alt]; 1154 | clwb(&(leaf_sibp->next[0])); sfence(); 1155 | 1156 | leaf_sibp->lock=0; // lock bit is not protected. 1157 | // It will be reset in recovery 1158 | } 1159 | 1160 | /* or it is the first child, so let's modify the first_leaf */ 1161 | else { 1162 | tree_meta->setFirstLeaf(lp->next[lp->alt]); // the method calls clwb+sfence 1163 | } 1164 | 1165 | // free the deleted leaf node 1166 | nvmpool_free_node(lp); 1167 | 1168 | } // end of Part 2 1169 | 1170 | /* Part 3: non-leaf node */ 1171 | {bnode *p, *sibp, *parp; 1172 | int n, i, pos, r, lev; 1173 | 1174 | lev = 1; 1175 | 1176 | while (1) { 1177 | p = parray[lev]; 1178 | n = p->num(); 1179 | pos = ppos[lev]; 1180 | 1181 | /* if the node has more than 1 children, simply delete */ 1182 | if (n > 0) { 1183 | if (pos == 0) { 1184 | p->ch(0)= p->ch(1); 1185 | pos= 1; // move the rest 1186 | } 1187 | for (i=pos; ient[i]= p->ent[i+1]; 1188 | p->num()= n - 1; 1189 | sfence(); 1190 | // all changes are globally visible now 1191 | 1192 | // root is guaranteed to have 2 children 1193 | if ((p->num()==0) && (lev >= tree_meta->root_level)) // root 1194 | break; 1195 | 1196 | p->lock()= 0; 1197 | return; 1198 | } 1199 | 1200 | /* otherwise only 1 ptr */ 1201 | mempool_free_node(p); 1202 | 1203 | lev++; 1204 | } /* end of while */ 1205 | 1206 | // p==root has 1 child? so delete the root 1207 | tree_meta->root_level = tree_meta->root_level - 1; 1208 | tree_meta->tree_root = p->ch(0); // running transactions will abort 1209 | sfence(); 1210 | 1211 | mempool_free_node (p); 1212 | return; 1213 | } 1214 | } 1215 | 1216 | /* ----------------------------------------------------------------- * 1217 | randomize 1218 | * ----------------------------------------------------------------- */ 1219 | 1220 | void lbtree::randomize (Pointer8B pnode, int level) 1221 | {int i; 1222 | 1223 | if (level > 0) { 1224 | bnode *p= pnode; 1225 | for (int i=0; i<=p->num(); i++) { 1226 | randomize (p->ch(i), level-1); 1227 | } 1228 | } 1229 | else { 1230 | bleaf * lp = pnode; 1231 | 1232 | int pos[LEAF_KEY_NUM]; 1233 | int num= 0; 1234 | 1235 | // 1. get all entries 1236 | unsigned short bmp= lp->bitmap; 1237 | for (int i=0; ifgpt[pos[aa]], lp->fgpt[pos[bb]]); 1250 | swap(lp->ent[pos[aa]], lp->ent[pos[bb]]); 1251 | } 1252 | } 1253 | } 1254 | } 1255 | 1256 | /* ----------------------------------------------------------------- * 1257 | print 1258 | * ----------------------------------------------------------------- */ 1259 | void lbtree::print (Pointer8B pnode, int level) 1260 | { 1261 | if (level > 0) { 1262 | bnode *p= pnode; 1263 | 1264 | printf("%*cnonleaf lev=%d num=%d\n", 10+level*4, '+', level, p->num()); 1265 | 1266 | print (p->ch(0), level-1); 1267 | for (int i=1; i<=p->num(); i++) { 1268 | printf ("%*c%lld\n", 10+level*4, '+', p->k(i)); 1269 | print (p->ch(i), level-1); 1270 | } 1271 | } 1272 | else { 1273 | bleaf * lp = pnode; 1274 | 1275 | unsigned short bmp= lp->bitmap; 1276 | for (int i=0; ifgpt[i], lp->k(i)); 1279 | } 1280 | } 1281 | 1282 | bleaf * pnext= lp->nextSibling(); 1283 | if (pnext != NULL) { 1284 | int first_pos= bitScan(pnext->bitmap)-1; 1285 | printf ("->(%lld)\n", pnext->k(first_pos)); 1286 | } 1287 | else 1288 | printf ("->(null)\n"); 1289 | } 1290 | } 1291 | 1292 | /* ----------------------------------------------------------------- * 1293 | check structure integrity 1294 | * ----------------------------------------------------------------- */ 1295 | 1296 | /** 1297 | * get min and max key in the given leaf p 1298 | */ 1299 | void lbtree::getMinMaxKey (bleaf *p, key_type &min_key, key_type &max_key) 1300 | { 1301 | unsigned short bmp= p->bitmap; 1302 | max_key= MIN_KEY; 1303 | min_key= MAX_KEY; 1304 | 1305 | for (int i=0; ik(i) > max_key) max_key = p->k(i); 1308 | if (p->k(i) < min_key) min_key = p->k(i); 1309 | } 1310 | } 1311 | } 1312 | 1313 | void lbtree::checkFirstLeaf(void) 1314 | { 1315 | // get left-most leaf node 1316 | bnode *p= tree_meta->tree_root; 1317 | for (int i= tree_meta->root_level; i>0; i--) p= p->ch(0); 1318 | 1319 | if ((bleaf *)p != *(tree_meta->first_leaf)) { 1320 | printf("first leaf %p != %p\n", *(tree_meta->first_leaf), p); 1321 | exit(1); 1322 | } 1323 | } 1324 | 1325 | /** 1326 | * recursively check the subtree rooted at pnode 1327 | * 1328 | * If it encounters an error, the method will print an error message and exit. 1329 | * 1330 | * @param pnode the subtree root 1331 | * @param level the level of pnode 1332 | * @param start return the start key of this subtree 1333 | * @param end return the end key of this subtree 1334 | * @param ptr ptr is the leaf before this subtree. 1335 | * Upon return, ptr is the last leaf of this subtree. 1336 | */ 1337 | void lbtree::check (Pointer8B pnode, int level, key_type &start, key_type &end, bleaf * &ptr) 1338 | { 1339 | if (pnode.isNull()) { 1340 | printf ("level %d: null child pointer\n", level + 1); 1341 | exit (1); 1342 | } 1343 | 1344 | if (level == 0) { // leaf node 1345 | bleaf *lp = pnode; 1346 | 1347 | if (((unsigned long long)lp)%256 != 0) { 1348 | printf ("leaf(%p): not aligned at 256B\n", lp); exit (1); 1349 | } 1350 | 1351 | // check number of keys 1352 | if (lp->num() < 1) {// empty node! 1353 | printf ("leaf(%p): empty\n", lp); exit (1); 1354 | } 1355 | 1356 | // get min max 1357 | getMinMaxKey(lp, start, end); 1358 | 1359 | // check fingerprints 1360 | unsigned short bmp= lp->bitmap; 1361 | for (int i=0; ik(i)) != lp->fgpt[i]) { 1364 | printf ("leaf(%lld): hash code for %lld is wrong\n", start, lp->k(i)); 1365 | exit(1); 1366 | } 1367 | } 1368 | } 1369 | 1370 | // check lock bit 1371 | if (lp->lock != 0) { 1372 | printf ("leaf(%lld): lock bit == 1\n", start); 1373 | exit(1); 1374 | } 1375 | 1376 | // check sibling pointer 1377 | if ((ptr) && (ptr->nextSibling()!=lp)) { 1378 | printf ("leaf(%lld): sibling broken from previous node\n", start); 1379 | fflush(stdout); 1380 | 1381 | /* output more info */ 1382 | bleaf *pp= (bleaf *)ptr; 1383 | key_type ss, ee; 1384 | getMinMaxKey(pp, ss, ee); 1385 | printf("previous(%lld - %lld) -> ", ss, ee); 1386 | 1387 | pp= pp->nextSibling(); 1388 | if (pp == NULL) { 1389 | printf("nil\n"); 1390 | } 1391 | else { 1392 | getMinMaxKey(pp, ss, ee); 1393 | printf("(%lld - %lld)\n", ss, ee); 1394 | } 1395 | 1396 | exit(1); 1397 | } 1398 | 1399 | ptr= lp; 1400 | } 1401 | 1402 | else { // nonleaf node 1403 | key_type curstart, curend; 1404 | int i; 1405 | bleaf *curptr; 1406 | 1407 | bnode * p = pnode; 1408 | 1409 | if (((unsigned long long)p)%64!= 0) { 1410 | printf ("nonleaf level %d(%p): not aligned at 64B\n", level, p); exit (1); 1411 | } 1412 | 1413 | // check num of keys 1414 | if (p->num()<0) { 1415 | printf ("nonleaf level %d(%p): num<0\n", level, p); exit (1); 1416 | } 1417 | 1418 | // check child 0 1419 | curptr = ptr; 1420 | check (p->ch(0), level-1, curstart, curend, curptr); 1421 | start = curstart; 1422 | if (p->num()>=1 && curend >= p->k(1)) { 1423 | printf ("nonleaf level %d(%lld): key order wrong at child 0\n", level, p->k(1)); 1424 | exit (1); 1425 | } 1426 | 1427 | // check child 1..num-1 1428 | for (i=1; inum(); i++) { 1429 | check (p->ch(i), level-1, curstart, curend, curptr); 1430 | if (!(p->k(i)<=curstart && curendk(i+1)) ) { 1431 | printf ("nonleaf level %d(%lld): key order wrong at child %d(%lld)\n", 1432 | level, p->k(1), i, p->k(i)); 1433 | exit (1); 1434 | } 1435 | } 1436 | 1437 | // check child num (when num>=1) 1438 | if (i == p->num()) { 1439 | check (p->ch(i), level-1, curstart, curend, curptr); 1440 | if (curstart < p->k(i)) { 1441 | printf ("nonleaf level %d(%lld): key order wrong at last child %d(%lld)\n", 1442 | level, p->k(1), i, p->k(i)); 1443 | exit (1); 1444 | } 1445 | } 1446 | end = curend; 1447 | 1448 | // check lock bit 1449 | if (p->lock() != 0) { 1450 | printf ("nonleaf level %d(%lld): lock bit is set\n", level, p->k(1)); 1451 | exit(1); 1452 | } 1453 | 1454 | ptr = curptr; 1455 | } 1456 | } 1457 | 1458 | 1459 | /* ------------------------------------------------------------------------- */ 1460 | /* driver */ 1461 | /* ------------------------------------------------------------------------- */ 1462 | tree * initTree(void *nvm_addr, bool recover) 1463 | { 1464 | tree *mytree = new lbtree(nvm_addr, recover); 1465 | return mytree; 1466 | } 1467 | 1468 | int main (int argc, char *argv[]) 1469 | { 1470 | printf("NON_LEAF_KEY_NUM= %d, LEAF_KEY_NUM= %d, nonleaf size= %lu, leaf size= %lu\n", 1471 | NON_LEAF_KEY_NUM, LEAF_KEY_NUM, sizeof(bnode), sizeof(bleaf)); 1472 | assert((sizeof(bnode) == NONLEAF_SIZE)&&(sizeof(bleaf) == LEAF_SIZE)); 1473 | 1474 | initUseful(); 1475 | 1476 | return parse_command (argc, argv); 1477 | } 1478 | -------------------------------------------------------------------------------- /lbtree-src/lbtree.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lbtree.h 3 | * @author Shimin Chen , Jihang Liu, Leying Chen 4 | * @version 1.0 5 | * 6 | * @section LICENSE 7 | * 8 | * TBD 9 | * 10 | * @section DESCRIPTION 11 | * 12 | * 13 | * The class implements the LB+-Tree. 14 | * 15 | * Non-leaf nodes are in DRAM. They are normal B+-Tree nodes. 16 | * Leaf nodes are in NVM. 17 | */ 18 | 19 | #ifndef _LBTREE_H 20 | #define _LBTREE_H 21 | /* ---------------------------------------------------------------------- */ 22 | 23 | #include "tree.h" 24 | /* ---------------------------------------------------------------------- */ 25 | 26 | /* In a non-leaf, there are NON_LEAF_KEY_NUM keys and NON_LEAF_KEY_NUM+1 27 | * child pointers. 28 | */ 29 | #define NON_LEAF_KEY_NUM (NONLEAF_SIZE/(KEY_SIZE+POINTER_SIZE)-1) 30 | 31 | /* In a leaf, there are 16B header, 14x16B entries, 2x8B sibling pointers. 32 | */ 33 | #if LEAF_SIZE != 256 34 | #error "LB+-Tree requires leaf node size to be 256B." 35 | #endif 36 | 37 | #define LEAF_KEY_NUM (14) 38 | 39 | /* ---------------------------------------------------------------------- */ 40 | /** 41 | * Pointer8B defines a class that can be assigned to either bnode or bleaf. 42 | */ 43 | class Pointer8B { 44 | public: 45 | unsigned long long value; /* 8B to contain a pointer */ 46 | 47 | public: 48 | Pointer8B(){} 49 | 50 | Pointer8B(const void *ptr) 51 | { value= (unsigned long long)ptr; } 52 | 53 | Pointer8B(const Pointer8B & p) 54 | { value= p.value; } 55 | 56 | Pointer8B & operator= (const void * ptr) 57 | { 58 | value= (unsigned long long)ptr; 59 | return *this; 60 | } 61 | Pointer8B & operator= (const Pointer8B & p) 62 | { 63 | value= p.value; 64 | return *this; 65 | } 66 | 67 | bool operator== (const void * ptr){ 68 | bool result = (value==(unsigned long long)ptr); 69 | return result; 70 | } 71 | bool operator== (const Pointer8B & p){ 72 | bool result = (value==p.value); 73 | return result; 74 | } 75 | 76 | 77 | operator void*() { return (void *)value; } 78 | operator char*() { return (char *)value; } 79 | operator struct bnode *() { return (struct bnode *)value;} 80 | operator struct bleaf *() { return (struct bleaf *)value;} 81 | operator unsigned long long () { return value;} 82 | 83 | bool isNull(void) {return (value==0);} 84 | 85 | void print(void) { printf("%llx\n", value); } 86 | 87 | }; // Pointer8B 88 | 89 | /** 90 | * An IdxEntry consists of a key and a pointer. 91 | */ 92 | typedef struct IdxEntry{ 93 | key_type k; 94 | Pointer8B ch; 95 | } IdxEntry; 96 | 97 | /** 98 | * bnodeMeta: the 8B meta data in Non-leaf node 99 | */ 100 | typedef struct bnodeMeta {/* 8B */ 101 | int lock; /* lock bit for concurrency control */ 102 | int num; /* number of keys */ 103 | } bnodeMeta; 104 | 105 | /** 106 | * bnode: non-leaf node 107 | * 108 | * metadata (i.e. k(0)) 109 | * 110 | * k(1) .. k(NON_LEAF_KEY_NUM) 111 | * 112 | * ch(0), ch(1) .. ch(NON_LEAF_KEY_NUM) 113 | */ 114 | class bnode{ 115 | public: 116 | IdxEntry ent[NON_LEAF_KEY_NUM + 1]; 117 | public: 118 | key_type & k(int idx) { return ent[idx].k; } 119 | Pointer8B & ch(int idx) { return ent[idx].ch; } 120 | 121 | char * chEndAddr(int idx) { 122 | return (char *)&(ent[idx].ch)+sizeof(Pointer8B)-1; 123 | } 124 | 125 | int & num(void) { return ((bnodeMeta *)&(ent[0].k))->num;} 126 | int & lock(void) { return ((bnodeMeta *)&(ent[0].k))->lock;} 127 | }; // bnode 128 | 129 | typedef union bleafMeta { 130 | unsigned long long word8B[2]; 131 | struct { 132 | uint16_t bitmap:14; 133 | uint16_t lock :1; 134 | uint16_t alt :1; 135 | unsigned char fgpt[LEAF_KEY_NUM]; /* fingerprints */ 136 | } v; 137 | } bleafMeta; 138 | 139 | /** 140 | * bleaf: leaf node 141 | * 142 | * We guarantee that each leaf must have >=1 key. 143 | */ 144 | class bleaf { 145 | public: 146 | uint16_t bitmap:14; 147 | uint16_t lock :1; 148 | uint16_t alt :1; 149 | unsigned char fgpt[LEAF_KEY_NUM]; /* fingerprints */ 150 | IdxEntry ent[LEAF_KEY_NUM]; 151 | bleaf * next[2]; 152 | 153 | public: 154 | key_type & k(int idx) { return ent[idx].k; } 155 | Pointer8B & ch(int idx) { return ent[idx].ch; } 156 | 157 | int num() {return countBit(bitmap);} 158 | bleaf * nextSibling() {return next[alt];} 159 | 160 | bool isFull(void) { return (bitmap == 0x3fff); } 161 | 162 | void setBothWords(bleafMeta *m) { 163 | bleafMeta * my_meta= (bleafMeta *)this; 164 | my_meta->word8B[1]= m->word8B[1]; 165 | my_meta->word8B[0]= m->word8B[0]; 166 | } 167 | 168 | void setWord0(bleafMeta *m) { 169 | bleafMeta * my_meta= (bleafMeta *)this; 170 | my_meta->word8B[0]= m->word8B[0]; 171 | } 172 | 173 | }; // bleaf 174 | 175 | /* ---------------------------------------------------------------------- */ 176 | 177 | class treeMeta { 178 | public: 179 | int root_level; // leaf: level 0, parent of leaf: level 1 180 | Pointer8B tree_root; 181 | bleaf ** first_leaf; // on NVM 182 | 183 | public: 184 | treeMeta(void *nvm_address, bool recover=false) 185 | { 186 | root_level = 0; 187 | tree_root=NULL; 188 | first_leaf= (bleaf **) nvm_address; 189 | 190 | if (! recover) setFirstLeaf(NULL); 191 | } 192 | 193 | void setFirstLeaf(bleaf * leaf) 194 | { 195 | *first_leaf= leaf; 196 | clwb(first_leaf); sfence(); 197 | } 198 | 199 | }; // treeMeta 200 | 201 | 202 | /* ---------------------------------------------------------------------- */ 203 | 204 | class lbtree: public tree { 205 | public: // root and level 206 | 207 | treeMeta * tree_meta; 208 | 209 | public: 210 | lbtree(void *nvm_address, bool recover=false) 211 | {tree_meta= new treeMeta(nvm_address, recover); 212 | if (!tree_meta) {perror("new"); exit(1);} 213 | } 214 | 215 | ~lbtree() 216 | {delete tree_meta;} 217 | 218 | private: 219 | int bulkloadSubtree(keyInput *input, int start_key, int num_key, 220 | float bfill, int target_level, 221 | Pointer8B pfirst[], int n_nodes[]); 222 | 223 | int bulkloadToptree(Pointer8B ptrs[], key_type keys[], int num_key, 224 | float bfill, int cur_level, int target_level, 225 | Pointer8B pfirst[], int n_nodes[]); 226 | 227 | void getMinMaxKey(bleaf *p, key_type &min_key, key_type &max_key); 228 | 229 | void getKeyPtrLevel(Pointer8B pnode, int pnode_level, key_type left_key, 230 | int target_level, Pointer8B ptrs[], key_type keys[], int &num_nodes, 231 | bool free_above_level_nodes); 232 | 233 | // sort pos[start] ... pos[end] (inclusively) 234 | void qsortBleaf(bleaf *p, int start, int end, int pos[]); 235 | 236 | public: 237 | // bulkload a tree and return the root level 238 | // use multiple threads to do the bulkloading 239 | int bulkload (int keynum, keyInput *input, float bfill); 240 | 241 | void randomize (Pointer8B pnode, int level); 242 | void randomize() 243 | { 244 | srand48(12345678); 245 | randomize(tree_meta->tree_root, tree_meta->root_level); 246 | } 247 | 248 | // given a search key, perform the search operation 249 | // return the leaf node pointer and the position within leaf node 250 | void * lookup (key_type key, int *pos); 251 | 252 | void * get_recptr (void *p, int pos) 253 | { 254 | return ((bleaf *)p)->ch(pos); 255 | } 256 | 257 | // insert (key, ptr) 258 | void insert (key_type key, void *ptr); 259 | 260 | // delete key 261 | void del (key_type key); 262 | 263 | private: 264 | void print (Pointer8B pnode, int level); 265 | void check (Pointer8B pnode, int level, key_type &start, key_type &end, bleaf* &ptr); 266 | void checkFirstLeaf(void); 267 | 268 | public: 269 | void print () 270 | { 271 | print (tree_meta->tree_root, tree_meta->root_level); 272 | } 273 | 274 | void check (key_type *start, key_type *end) 275 | { 276 | bleaf *ptr= NULL; 277 | check (tree_meta->tree_root, tree_meta->root_level, *start, *end, ptr); 278 | checkFirstLeaf(); 279 | } 280 | 281 | int level () {return tree_meta->root_level;} 282 | 283 | }; // lbtree 284 | 285 | /* ---------------------------------------------------------------------- */ 286 | #endif /* _LBTREE_H */ 287 | --------------------------------------------------------------------------------