├── src ├── HazardManager.cpp ├── main.cpp ├── Results.cpp ├── memory.cpp ├── test.cpp ├── genzipf.cpp └── bench.cpp ├── include ├── test.hpp ├── bench.hpp ├── Utils.hpp ├── hash.hpp ├── tree_type_traits.hpp ├── Results.hpp ├── file_distribution.hpp ├── skiplist │ └── SkipList.hpp ├── HazardManager.hpp ├── nbbst │ └── NBBST.hpp ├── avltree │ └── AVLTree.hpp ├── cbtree │ └── CBTree.hpp └── lfmst │ └── MultiwaySearchTree.hpp ├── .gitignore ├── sonar-project.properties ├── CMakeLists.txt └── README.rst /src/HazardManager.cpp: -------------------------------------------------------------------------------- 1 | #include "HazardManager.hpp" 2 | 3 | __thread unsigned int thread_num; 4 | -------------------------------------------------------------------------------- /include/test.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEST_TREES 2 | #define TEST_TREES 3 | 4 | /*! 5 | * Launch all the tests. 6 | */ 7 | void test(); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | bin/* 3 | *.o 4 | *.swp 5 | *.swo 6 | CMakeCache.txt 7 | CMakeFiles 8 | Makefile 9 | cmake_install.cmake 10 | graphs 11 | zipf 12 | -------------------------------------------------------------------------------- /include/bench.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BENCH_TREE 2 | #define BENCH_TREE 3 | 4 | /*! 5 | * Bench the different structures 6 | */ 7 | void bench(); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=btrees 2 | sonar.projectName=btrees 3 | sonar.projectVersion=1.0 4 | 5 | sonar.sourceEncoding=UTF-8 6 | sonar.sources=src,include 7 | sonar.language=c++ 8 | 9 | sonar.cxx.cppcheck.reportPath=cppcheck_report.xml -------------------------------------------------------------------------------- /include/Utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTILS 2 | #define UTILS 3 | 4 | /*! 5 | * Compare and Swap a pointer. 6 | * \param ptr The pointer to swap. 7 | * \param old The expected value. 8 | * \param value The new value to set. 9 | * \return true if the CAS suceeded, otherwise false. 10 | */ 11 | template 12 | bool inline CASPTR(T** ptr, T* old, T* value){ 13 | return __sync_bool_compare_and_swap(ptr, old, value); 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/hash.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HASH 2 | #define HASH 3 | 4 | /*! 5 | * Hash the given value depending on its type. 6 | * This function is made to be template-specialized. 7 | * \param T The type of value to hash. 8 | * \param value The value to hash. 9 | */ 10 | template 11 | int hash(T value); 12 | 13 | /*! 14 | * Specialization of hash for the int type. 15 | */ 16 | template<> 17 | inline int hash(int value){ 18 | return value; 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /include/tree_type_traits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TREE_TYPE_TRAITS 2 | #define TREE_TYPE_TRAITS 3 | 4 | #include "nbbst/NBBST.hpp" 5 | #include "lfmst/MultiwaySearchTree.hpp" 6 | 7 | template 8 | struct tree_type_traits { 9 | static const bool balanced = true; 10 | }; 11 | 12 | template 13 | struct tree_type_traits> { 14 | static const bool balanced = false; 15 | }; 16 | 17 | template 18 | bool is_balanced(){ 19 | return tree_type_traits::balanced; 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "test.hpp" 4 | #include "bench.hpp" 5 | 6 | /*! 7 | * Launch the test indicated by the arguments. 8 | */ 9 | int main(int argc, const char* argv[]) { 10 | std::cout << "Concurrent Binary Trees test" << std::endl; 11 | 12 | //By default launch perf test 13 | if(argc == 1){ 14 | bench(); 15 | } else if(argc == 2) { 16 | std::string arg = argv[1]; 17 | 18 | if(arg == "-perf"){ 19 | bench(); 20 | } else if(arg == "-test"){ 21 | test(); 22 | } else if(arg == "-all"){ 23 | test(); 24 | bench(); 25 | } else { 26 | std::cout << "Unrecognized option " << arg << std::endl; 27 | } 28 | } else { 29 | std::cout << "Too many arguments" << std::endl; 30 | } 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /include/Results.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RESULTS 2 | #define RESULTS 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef std::map>> results_map; 9 | typedef std::map> stats_map; 10 | typedef std::map currents_map; 11 | 12 | /*! 13 | * Simple class to store the results of a bench and write them to a file. 14 | */ 15 | class Results { 16 | public: 17 | void start(const std::string& name); 18 | void set_max(int max); 19 | 20 | void add_result(const std::string& structure, unsigned long value); 21 | 22 | void finish(); 23 | 24 | private: 25 | results_map values; 26 | stats_map stats; 27 | currents_map current; 28 | currents_map level; 29 | 30 | std::string name; 31 | int max; 32 | 33 | void compute_stats(); 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/file_distribution.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILE_DISTRIBUTION 2 | #define FILE_DISTRIBUTION 3 | 4 | #include 5 | #include 6 | 7 | /*! 8 | * \brief A sample random distribution that takes its value in a file. 9 | * All the values of the file will be loaded in memory at construction time of the distribution. 10 | * \param Type The type of value contained in the file. 11 | */ 12 | template 13 | class file_distribution { 14 | public: 15 | typedef Type result_type; 16 | 17 | private: 18 | std::vector values; 19 | 20 | std::uniform_int_distribution distribution; 21 | 22 | public: 23 | file_distribution(const std::string& file, unsigned int size){ 24 | std::ifstream stream(file.c_str()); 25 | 26 | if(!stream){ 27 | throw "Unable to open the file " + file; 28 | } 29 | 30 | for(unsigned int i = 0; i < size; ++i){ 31 | int value = 0; 32 | stream >> value; 33 | values.push_back(value); 34 | } 35 | 36 | distribution = std::uniform_int_distribution(0, size - 1); 37 | } 38 | 39 | result_type operator()(unsigned int i){ 40 | return values[i % values.size()]; 41 | } 42 | 43 | template 44 | result_type operator()(Engine& eng){ 45 | return values[distribution(eng)]; 46 | } 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project("BTrees Perf test") 4 | 5 | #Set the version number 6 | set(VERSION_MAJOR 1) 7 | set(VERSION_MINOR 0) 8 | set(VERSION_PATCH 0) 9 | 10 | #Activate warnings 11 | if(MSVC) 12 | if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") 13 | string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 14 | else() 15 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 16 | endif() 17 | elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) 18 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -march=native -funroll-loops -pthread -std=c++0x -pedantic -Wall -Wextra -Wno-long-long") 19 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -pthread -std=c++0x -pedantic -Wall -Wextra -Wno-long-long") 20 | endif() 21 | 22 | set(EXECUTABLE_OUTPUT_PATH bin) 23 | 24 | include_directories(include) 25 | 26 | file( 27 | GLOB_RECURSE 28 | btrees_memory_source_files 29 | src/*.cpp 30 | ) 31 | 32 | file( 33 | GLOB_RECURSE 34 | btrees_source_files 35 | src/*.cpp 36 | ) 37 | 38 | file(GLOB to_remove src/genzipf.cpp) 39 | list(REMOVE_ITEM btrees_memory_source_files ${to_remove}) 40 | list(REMOVE_ITEM btrees_source_files ${to_remove}) 41 | 42 | file(GLOB to_remove src/memory.cpp) 43 | list(REMOVE_ITEM btrees_source_files ${to_remove}) 44 | 45 | file(GLOB to_remove src/main.cpp) 46 | list(REMOVE_ITEM btrees_memory_source_files ${to_remove}) 47 | 48 | add_executable(btrees ${btrees_source_files}) 49 | add_executable(memory ${btrees_memory_source_files}) 50 | add_executable(gen_zip src/genzipf.cpp) 51 | 52 | target_link_libraries(btrees rt) 53 | target_link_libraries(gen_zip m) 54 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Performance comparison of concurrent Binary Search Trees in C++ 2 | =============================================================== 3 | 4 | This project aims to compare several concurrent implementation of Binary Search 5 | Tree in C++: 6 | 7 | * SkipList 8 | * Non-Blocking Binary Search Trees 9 | * Optimistic AVL Tree 10 | * Lock-Free Multiway Search Tree 11 | * Counter-Based Tree 12 | 13 | Note: The code has only been tested on Intel processors. It is possible that 14 | there are some differences with other processors. It has only been tested under 15 | Linux and with GCC. This application will not build under Windows and is 16 | unlikely to build under another compiler. 17 | 18 | Build 19 | ----- 20 | 21 | CMake is used to build the project: 22 | 23 | cmake . 24 | make -j9 25 | 26 | Warning: GCC 4.6 at least is necessary to build this project. 27 | 28 | Launch tests 29 | ------------ 30 | 31 | The tests can be launched easily: 32 | 33 | ./bin/btrees -test 34 | 35 | Note: The full tests can take about 30 minutes to complete on not-very modern 36 | computer and can takes more than 2GB of memory. 37 | 38 | Launch memory benchmark 39 | ----------------------- 40 | 41 | The memory benchmark is separated in two parts. The first (low) tests the 42 | memory consumption with range in [0, size] and the second (high) tests the 43 | memory consumption on higher range [0, INT_MAX]: 44 | 45 | ./bin/memory -high 46 | ./bin/memory -low 47 | 48 | Note: The memory benchmark needs at least 6GB of memory to run. 49 | 50 | Launch the benchmark 51 | -------------------- 52 | 53 | The full benchmark can be run like this: 54 | 55 | ./bin/btrees -test 56 | 57 | Note: Even on modern computer, the benchmark may take more than 10 hours to 58 | complete and needs several GB of memory. On old hardware, it can easily takes 59 | about 24 hours to complete. 60 | -------------------------------------------------------------------------------- /src/Results.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Results.hpp" 5 | 6 | void Results::start(const std::string& name){ 7 | this->name = name; 8 | this->max = -1; 9 | 10 | values.clear(); 11 | current.clear(); 12 | stats.clear(); 13 | level.clear(); 14 | } 15 | 16 | void Results::set_max(int max){ 17 | this->max = max; 18 | } 19 | 20 | void Results::add_result(const std::string& structure, unsigned long value){ 21 | if(current[structure] == max){ 22 | ++level[structure]; 23 | current[structure] = 0; 24 | } 25 | 26 | if(values.find(structure) == values.end()){ 27 | values[structure].resize(max); 28 | } 29 | 30 | values[structure][current[structure]].push_back(value); 31 | 32 | ++current[structure]; 33 | } 34 | 35 | void Results::compute_stats(){ 36 | auto it = values.begin(); 37 | auto end = values.end(); 38 | 39 | while(it != end){ 40 | auto impl = it->first; 41 | auto data = it->second; 42 | 43 | for(unsigned int i = 0; i < data.size(); ++i){ 44 | //If it's not the case, max has been configured too high 45 | if(data[i].size() > 0){ 46 | unsigned long sum = 0; 47 | 48 | for(auto j : data[i]){ 49 | sum += j; 50 | } 51 | 52 | stats[impl].push_back(sum / data[i].size()); 53 | } 54 | } 55 | 56 | ++it; 57 | } 58 | } 59 | 60 | void Results::finish(){ 61 | compute_stats(); 62 | 63 | auto it = stats.begin(); 64 | auto end = stats.end(); 65 | 66 | while(it != end){ 67 | std::string file = "graphs/" + it->first + "-" + name + ".dat"; 68 | 69 | std::ofstream stream; 70 | stream.open(file.c_str()); 71 | 72 | if(!stream){ 73 | throw "Unable to open the file " + file; 74 | } 75 | 76 | for(unsigned int i = 0; i < it->second.size(); ++i){ 77 | stream << (i+1) << " " << it->second[i] << std::endl; 78 | } 79 | 80 | stream.close(); 81 | 82 | ++it; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/memory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void* (*old_malloc_hook)(size_t, const void*); 6 | static void (*old_free_hook)(void*, const void*); 7 | 8 | static unsigned long allocated = 0; 9 | 10 | static bool end = false; 11 | static std::unordered_map sizes; 12 | 13 | static void* rqmalloc_hook(size_t size, const void* source); 14 | static void rqfree_hook(void* memory, const void* source); 15 | 16 | static void* rqmalloc_hook(size_t size, const void* /* source*/){ 17 | void* result; 18 | 19 | __malloc_hook = old_malloc_hook; 20 | __free_hook = old_free_hook; 21 | 22 | result = malloc(size); 23 | 24 | old_malloc_hook = __malloc_hook; 25 | old_free_hook = __free_hook; 26 | 27 | if(!end){ 28 | sizes[result] = size; 29 | allocated += size; 30 | } 31 | 32 | __malloc_hook = rqmalloc_hook; 33 | __free_hook = rqfree_hook; 34 | 35 | return result; 36 | } 37 | 38 | static void rqfree_hook(void* memory, const void* /* source */){ 39 | __malloc_hook = old_malloc_hook; 40 | __free_hook = old_free_hook; 41 | 42 | free(memory); 43 | 44 | old_malloc_hook = __malloc_hook; 45 | old_free_hook = __free_hook; 46 | 47 | if(!end){ 48 | allocated -= sizes[memory]; 49 | } 50 | 51 | __malloc_hook = rqmalloc_hook; 52 | __free_hook = rqfree_hook; 53 | } 54 | 55 | void memory_init(){ 56 | old_malloc_hook = __malloc_hook; 57 | old_free_hook = __free_hook; 58 | __malloc_hook = rqmalloc_hook; 59 | __free_hook = rqfree_hook; 60 | } 61 | 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | 69 | #include "Results.hpp" 70 | 71 | //Include all the trees implementations 72 | #include "skiplist/SkipList.hpp" 73 | #include "nbbst/NBBST.hpp" 74 | #include "avltree/AVLTree.hpp" 75 | #include "lfmst/MultiwaySearchTree.hpp" 76 | #include "cbtree/CBTree.hpp" 77 | 78 | /*! 79 | * Launch the memory test on the given Tree. 80 | * \param Tree The type of the tree. 81 | * \param name The name of the tree. 82 | * \param size The number of elements to insert into the tree. 83 | * \param results The results to fill. 84 | */ 85 | template 86 | void memory(const std::string& name, unsigned int size, Results& results){ 87 | std::vector elements; 88 | for(unsigned int i = 0; i < size; ++i){ 89 | elements.push_back(i); 90 | } 91 | 92 | //Use random insertion in order to support non-balanced version 93 | random_shuffle(elements.begin(), elements.end()); 94 | 95 | //For now on, count all the allocations 96 | allocated = 0; 97 | 98 | Tree* alloc_tree = new Tree(); 99 | Tree& tree = *alloc_tree; 100 | 101 | //Fill the tree 102 | for(unsigned int i = 0; i < size; ++i){ 103 | tree.add(elements[i]); 104 | } 105 | 106 | unsigned long usage = allocated; 107 | 108 | std::cout << name << "-" << size << " is using " << (usage / 1024) << " KB" << std::endl; 109 | results.add_result(name, (usage / 1024.0)); 110 | 111 | //Empty the tree 112 | for(unsigned int i = 0; i < size; ++i){ 113 | tree.remove(i); 114 | } 115 | 116 | delete alloc_tree; 117 | } 118 | 119 | /*! 120 | * Launch the memory test on the given Tree. The elements are taken in the range [0, INT_MAX - 1]. 121 | * \param Tree The type of the tree. 122 | * \param name The name of the tree. 123 | * \param size The number of elements to insert into the tree. 124 | * \param results The results to fill. 125 | */ 126 | template 127 | void memory_high(const std::string& name, unsigned int size, Results& results){ 128 | std::mt19937_64 engine(time(0)); 129 | 130 | //Note: The max() value cannot be managed by some structure 131 | std::uniform_int_distribution valueDistribution(0, std::numeric_limits::max() - 1); 132 | auto valueGenerator = std::bind(valueDistribution, engine); 133 | 134 | std::set elements; 135 | while(elements.size() < size){ 136 | elements.insert(valueGenerator()); 137 | } 138 | 139 | std::vector vector_elements; 140 | for(auto i : elements){ 141 | vector_elements.push_back(i); 142 | } 143 | 144 | //It is necessary to shuffle as the iteration through set is sorted 145 | random_shuffle(vector_elements.begin(), vector_elements.end()); 146 | 147 | //For now on, count all the allocations 148 | allocated = 0; 149 | 150 | Tree* alloc_tree = new Tree(); 151 | Tree& tree = *alloc_tree; 152 | 153 | for(auto i : vector_elements){ 154 | tree.add(i); 155 | } 156 | 157 | unsigned long usage = allocated; 158 | 159 | std::cout << name << "-" << size << " is using " << (usage / 1024) << " KB" << std::endl; 160 | results.add_result(name, (usage / 1024.0)); 161 | 162 | //Empty the tree 163 | for(auto i : vector_elements){ 164 | tree.remove(i); 165 | } 166 | 167 | delete alloc_tree; 168 | } 169 | 170 | /*! 171 | * Launch the memory tests depending on the arguments 172 | */ 173 | int main(int argc, const char* argv[]) { 174 | memory_init(); 175 | 176 | std::vector little_sizes = {1000, 10000, 100000}; 177 | std::vector big_sizes = {1000000, 10000000}; 178 | 179 | thread_num = 0; 180 | 181 | if(argc == 1){ 182 | std::cout << "low or high argument needed" << std::endl; 183 | } else { 184 | std::string arg = argv[1]; 185 | 186 | if(arg == "low"){ 187 | std::cout << "Test the normal memory consumption of each version" << std::endl; 188 | 189 | Results results; 190 | results.start("memory-little"); 191 | results.set_max(3); 192 | 193 | for(auto size : little_sizes){ 194 | memory>("skiplist", size, results); 195 | memory>("nbbst", size, results); 196 | memory>("lfmst", size, results); 197 | memory>("avltree", size, results); 198 | memory>("cbtree", size, results); 199 | } 200 | 201 | results.finish(); 202 | 203 | results.start("memory-big"); 204 | results.set_max(2); 205 | 206 | for(auto size : big_sizes){ 207 | memory>("skiplist", size, results); 208 | memory>("nbbst", size, results); 209 | memory>("lfmst", size, results); 210 | memory>("avltree", size, results); 211 | memory>("cbtree", size, results); 212 | } 213 | 214 | results.finish(); 215 | } else if(arg == "high"){ 216 | std::cout << "Test the high memory consumption of each version" << std::endl; 217 | 218 | Results results; 219 | results.start("memory-little-high"); 220 | results.set_max(3); 221 | 222 | for(auto size : little_sizes){ 223 | memory_high>("skiplist", size, results); 224 | memory_high>("nbbst", size, results); 225 | memory_high>("lfmst", size, results); 226 | memory_high>("avltree", size, results); 227 | memory_high>("cbtree", size, results); 228 | } 229 | 230 | results.finish(); 231 | 232 | results.start("memory-big-high"); 233 | results.set_max(2); 234 | 235 | for(auto size : big_sizes){ 236 | memory_high>("skiplist", size, results); 237 | memory_high>("nbbst", size, results); 238 | memory_high>("lfmst", size, results); 239 | memory_high>("avltree", size, results); 240 | memory_high>("cbtree", size, results); 241 | } 242 | 243 | results.finish(); 244 | } else { 245 | std::cout << "incorrect argument" << std::endl; 246 | } 247 | } 248 | 249 | end = true; 250 | 251 | return 0; 252 | } 253 | -------------------------------------------------------------------------------- /include/skiplist/SkipList.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SKIP_LIST 2 | #define SKIP_LIST 3 | 4 | #include "hash.hpp" 5 | #include "Utils.hpp" 6 | #include "HazardManager.hpp" 7 | 8 | #define MAX_LEVEL 24 //Should be choosen as log(1/p)(n) 9 | #define P 0.5 //probability for randomLevel (geometric distribution) 10 | 11 | namespace skiplist { 12 | 13 | struct Node { 14 | int key; 15 | int topLevel; 16 | Node** next; 17 | 18 | Node(){ 19 | //Fill the array with null pointers 20 | next = static_cast(calloc(MAX_LEVEL + 1, sizeof(Node *))); 21 | } 22 | 23 | ~Node(){ 24 | free(next); 25 | } 26 | }; 27 | 28 | inline Node* Unmark(Node* node){ 29 | return reinterpret_cast(reinterpret_cast(node) & (~0l - 1)); 30 | } 31 | 32 | inline Node* Mark(Node* node){ 33 | return reinterpret_cast(reinterpret_cast(node) | 0x1); 34 | } 35 | 36 | inline bool IsMarked(Node* node){ 37 | return reinterpret_cast(node) & 0x1; 38 | } 39 | 40 | template 41 | class SkipList { 42 | public: 43 | SkipList(); 44 | ~SkipList(); 45 | 46 | bool add(T value); 47 | bool remove(T value); 48 | bool contains(T value); 49 | 50 | private: 51 | int randomLevel(); 52 | bool find(int key, Node** preds, Node** succs); 53 | 54 | Node* newNode(int key, int height); 55 | 56 | Node* head; 57 | Node* tail; 58 | 59 | HazardManager hazard; 60 | 61 | std::mt19937_64 engine; 62 | std::geometric_distribution distribution; 63 | }; 64 | 65 | template 66 | Node* SkipList::newNode(int key, int height){ 67 | Node* node = hazard.getFreeNode(); 68 | 69 | node->key = key; 70 | node->topLevel = height; 71 | 72 | //Make sure everything gets set to null 73 | std::fill(node->next, node->next + MAX_LEVEL + 1, nullptr); 74 | 75 | return node; 76 | } 77 | 78 | template 79 | SkipList::SkipList() : engine(time(NULL)), distribution(P) { 80 | head = newNode(std::numeric_limits::min(), MAX_LEVEL); 81 | tail = newNode(std::numeric_limits::max(), 0); 82 | 83 | for(int i = 0; i < MAX_LEVEL + 1; ++i){ 84 | head->next[i] = tail; 85 | } 86 | } 87 | 88 | template 89 | SkipList::~SkipList(){ 90 | hazard.releaseNode(tail); 91 | hazard.releaseNode(head); 92 | } 93 | 94 | template 95 | int SkipList::randomLevel(){ 96 | int level = distribution(engine); 97 | 98 | return (level >= MAX_LEVEL) ? MAX_LEVEL : level; 99 | } 100 | 101 | template 102 | bool SkipList::add(T value){ 103 | int key = hash(value); 104 | int topLevel = randomLevel(); 105 | 106 | Node* preds[MAX_LEVEL + 1]; 107 | Node* succs[MAX_LEVEL + 1]; 108 | 109 | Node* newElement = newNode(key, topLevel); 110 | hazard.publish(newElement, 0); 111 | 112 | while(true){ 113 | if(find(key, preds, succs)){ 114 | hazard.releaseAll(); 115 | 116 | hazard.releaseNode(newElement); 117 | 118 | return false; 119 | } else { 120 | for(int level = 0; level <= topLevel; ++level){ 121 | newElement->next[level] = succs[level]; 122 | } 123 | 124 | hazard.publish(preds[0]->next[0], 1); 125 | hazard.publish(succs[0], 2); 126 | 127 | if(CASPTR(&preds[0]->next[0], succs[0], newElement)){ 128 | for(int level = 1; level <= topLevel; ++level){ 129 | while(true){ 130 | hazard.publish(preds[level]->next[level], 1); 131 | hazard.publish(succs[level], 2); 132 | 133 | if(CASPTR(&preds[level]->next[level], succs[level], newElement)){ 134 | break; 135 | } else { 136 | find(key, preds, succs); 137 | } 138 | } 139 | } 140 | 141 | hazard.releaseAll(); 142 | 143 | return true; 144 | } 145 | } 146 | } 147 | } 148 | 149 | template 150 | bool SkipList::remove(T value){ 151 | int key = hash(value); 152 | 153 | Node* preds[MAX_LEVEL + 1]; 154 | Node* succs[MAX_LEVEL + 1]; 155 | 156 | while(true){ 157 | if(!find(key, preds, succs)){ 158 | hazard.release(1); 159 | hazard.release(0); 160 | 161 | return false; 162 | } else { 163 | Node* nodeToRemove = succs[0]; 164 | hazard.publish(nodeToRemove, 0); 165 | 166 | for(int level = nodeToRemove->topLevel; level > 0; --level){ 167 | Node* succ = nullptr; 168 | do { 169 | succ = nodeToRemove->next[level]; 170 | hazard.publish(succ, 1); 171 | 172 | if(IsMarked(succ)){ 173 | break; 174 | } 175 | } while (!CASPTR(&nodeToRemove->next[level], succ, Mark(succ))); 176 | } 177 | 178 | while(true){ 179 | Node* succ = nodeToRemove->next[0]; 180 | hazard.publish(succ, 1); 181 | 182 | if(IsMarked(succ)){ 183 | break; 184 | } else if(CASPTR(&nodeToRemove->next[0], succ, Mark(succ))){ 185 | hazard.release(1); 186 | hazard.release(0); 187 | 188 | find(key, preds, succs); 189 | 190 | hazard.releaseNode(nodeToRemove); 191 | 192 | return true; 193 | } 194 | } 195 | } 196 | } 197 | } 198 | 199 | template 200 | bool SkipList::contains(T value){ 201 | int key = hash(value); 202 | 203 | Node* pred = head; 204 | Node* curr = nullptr; 205 | Node* succ = nullptr; 206 | 207 | for(int level = MAX_LEVEL; level >= 0; --level){ 208 | curr = Unmark(pred->next[level]); 209 | 210 | while(true){ 211 | succ = curr->next[level]; 212 | 213 | while(IsMarked(succ)){ 214 | curr = Unmark(curr->next[level]); 215 | succ = curr->next[level]; 216 | } 217 | 218 | if(curr->key < key){ 219 | pred = curr; 220 | curr = succ; 221 | } else { 222 | break; 223 | } 224 | } 225 | } 226 | 227 | bool found = curr->key == key; 228 | 229 | return found; 230 | } 231 | 232 | template 233 | bool SkipList::find(int key, Node** preds, Node** succs){ 234 | Node* pred = nullptr; 235 | Node* curr = nullptr; 236 | Node* succ = nullptr; 237 | 238 | retry: 239 | //We must do to release after the goto 240 | hazard.releaseAll(); 241 | 242 | pred = head; 243 | hazard.publish(pred, 0); 244 | 245 | for(int level = MAX_LEVEL; level >= 0; --level){ 246 | curr = pred->next[level]; 247 | hazard.publish(curr, 1); 248 | 249 | while(true){ 250 | if(IsMarked(curr)){ 251 | goto retry; 252 | } 253 | 254 | succ = curr->next[level]; 255 | hazard.publish(succ, 2); 256 | 257 | while(IsMarked(succ)){ 258 | if(!CASPTR(&pred->next[level], curr, Unmark(succ))){ 259 | goto retry; 260 | } 261 | 262 | curr = pred->next[level]; 263 | hazard.publish(curr, 1); 264 | 265 | if(IsMarked(curr)){ 266 | goto retry; 267 | } 268 | 269 | succ = curr->next[level]; 270 | hazard.publish(succ, 2); 271 | } 272 | 273 | if(curr->key < key){ 274 | pred = curr; 275 | hazard.publish(pred, 0); 276 | 277 | curr = succ; 278 | hazard.publish(curr, 1); 279 | } else { 280 | break; 281 | } 282 | } 283 | 284 | preds[level] = pred; 285 | succs[level] = curr; 286 | } 287 | 288 | bool found = curr->key == key; 289 | 290 | hazard.releaseAll(); 291 | 292 | return found; 293 | } 294 | 295 | } 296 | 297 | #endif 298 | -------------------------------------------------------------------------------- /src/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "test.hpp" 10 | #include "HazardManager.hpp" //To manipulate thread_num 11 | #include "tree_type_traits.hpp" 12 | 13 | //Include all the trees implementations 14 | #include "skiplist/SkipList.hpp" 15 | #include "nbbst/NBBST.hpp" 16 | #include "avltree/AVLTree.hpp" 17 | #include "lfmst/MultiwaySearchTree.hpp" 18 | #include "cbtree/CBTree.hpp" 19 | 20 | //Number of nodes inserted in single-threaded mode 21 | #define ST_N 100000 22 | 23 | //Number of nodes inserted in multi-threaded mode (for up to 32 threads) 24 | #define MT_N 100000 //Warning: This number is inserted for each thread 25 | 26 | //Utility macros to print debug messages during the tests 27 | #define DEBUG_ENABLED true 28 | #define DEBUG(message) if(DEBUG_ENABLED) std::cout << message << std::endl; 29 | 30 | /*! 31 | * Launch a single threaded test on the given structure. 32 | * \param T The type of the structure. 33 | * \param name The name of the structure being tested. 34 | */ 35 | template 36 | void testST(const std::string& name){ 37 | std::cout << "Test single-threaded (with " << ST_N << " elements) " << name << std::endl; 38 | 39 | thread_num = 0; 40 | 41 | T tree; 42 | 43 | std::mt19937_64 engine(time(NULL)); 44 | 45 | //Note: The max() value cannot be handled by all data structure 46 | std::uniform_int_distribution distribution(0, std::numeric_limits::max() - 1); 47 | auto generator = std::bind(distribution, engine); 48 | 49 | DEBUG("Remove numbers in the empty tree"); 50 | 51 | for(unsigned int i = 0; i < ST_N; ++i){ 52 | auto number = generator(); 53 | 54 | assert(!tree.contains(number)); 55 | assert(!tree.remove(number)); 56 | } 57 | 58 | DEBUG("Insert sequential numbers"); 59 | 60 | unsigned int sequential_nodes = ST_N; 61 | 62 | if(!is_balanced()){ 63 | sequential_nodes /= 25; 64 | } 65 | 66 | for(unsigned int i = 0; i < sequential_nodes; ++i){ 67 | assert(!tree.contains(i)); 68 | assert(tree.add(i)); 69 | assert(tree.contains(i)); 70 | } 71 | 72 | DEBUG("Remove all the sequential numbers"); 73 | 74 | for(unsigned int i = 0; i < sequential_nodes; ++i){ 75 | assert(tree.contains(i)); 76 | assert(tree.remove(i)); 77 | assert(!tree.contains(i)); 78 | } 79 | 80 | DEBUG("Verify that the tree is empty"); 81 | 82 | for(unsigned int i = 0; i < sequential_nodes; ++i){ 83 | assert(!tree.contains(i)); 84 | } 85 | 86 | std::vector rand; 87 | 88 | DEBUG("Insert N random numbers in the tree"); 89 | 90 | for(unsigned int i = 0; i < ST_N; ++i){ 91 | int number = generator(); 92 | 93 | if(tree.contains(number)){ 94 | assert(!tree.add(number)); 95 | assert(tree.contains(number)); 96 | } else { 97 | assert(tree.add(number)); 98 | assert(tree.contains(number)); 99 | 100 | rand.push_back(number); 101 | } 102 | } 103 | 104 | DEBUG("Remove numbers not present in the tree"); 105 | 106 | for(unsigned int i = 0; i < ST_N; ++i){ 107 | int number = generator(); 108 | 109 | if(!tree.contains(number)){ 110 | assert(!tree.remove(number)); 111 | assert(!tree.contains(number)); 112 | } 113 | } 114 | 115 | DEBUG("Remove all the numbers in random order"); 116 | 117 | random_shuffle(rand.begin(), rand.end()); 118 | for(int number : rand){ 119 | assert(tree.contains(number)); 120 | assert(tree.remove(number)); 121 | } 122 | 123 | DEBUG("Remove numbers in the empty tree"); 124 | 125 | for(unsigned int i = 0; i < ST_N; ++i){ 126 | auto number = generator(); 127 | 128 | assert(!tree.contains(number)); 129 | assert(!tree.remove(number)); 130 | } 131 | 132 | std::cout << "Test passed successfully" << std::endl; 133 | } 134 | 135 | /*! 136 | * Launch the multithreaded tests on the given tree type. 137 | * \param T The type of tree to test. 138 | * \param Threads The number of threads. 139 | */ 140 | template 141 | void testMT(){ 142 | T tree; 143 | 144 | DEBUG("Insert and remove sequential numbers from the tree") 145 | 146 | int sequential_nodes = MT_N; 147 | 148 | if(!is_balanced()){ 149 | sequential_nodes /= 25; 150 | } 151 | 152 | std::vector pool; 153 | for(unsigned int i = 0; i < Threads; ++i){ 154 | pool.push_back(std::thread([sequential_nodes, &tree, i](){ 155 | thread_num = i; 156 | 157 | //Insert sequential numbers 158 | for(unsigned int j = i * sequential_nodes; j < (i + 1) * sequential_nodes; ++j){ 159 | assert(!tree.contains(j)); 160 | assert(tree.add(j)); 161 | assert(tree.contains(j)); 162 | } 163 | 164 | //Remove all the sequential numbers 165 | for(unsigned int j = i * sequential_nodes; j < (i + 1) * sequential_nodes; ++j){ 166 | assert(tree.contains(j)); 167 | assert(tree.remove(j)); 168 | assert(!tree.contains(j)); 169 | } 170 | })); 171 | } 172 | 173 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 174 | pool.clear(); 175 | 176 | DEBUG("Verify that all the numbers have been removed correctly") 177 | 178 | for(unsigned int i = 0; i < Threads; ++i){ 179 | pool.push_back(std::thread([sequential_nodes, &tree, i](){ 180 | thread_num = i; 181 | 182 | //Verify that every numbers has been removed correctly 183 | for(unsigned int j = 0; j < Threads * sequential_nodes; ++j){ 184 | assert(!tree.contains(j)); 185 | } 186 | })); 187 | } 188 | 189 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 190 | pool.clear(); 191 | 192 | std::vector fixed_points; 193 | 194 | std::mt19937_64 fixed_engine(time(NULL)); 195 | std::uniform_int_distribution fixed_distribution(0, std::numeric_limits::max() - 1); 196 | 197 | DEBUG("Compute the fixed points") 198 | 199 | while(fixed_points.size() < Threads){ 200 | auto value = fixed_distribution(fixed_engine); 201 | 202 | if(std::find(fixed_points.begin(), fixed_points.end(), value) == fixed_points.end()){ 203 | fixed_points.push_back(value); 204 | 205 | assert(tree.add(value)); 206 | } 207 | } 208 | 209 | DEBUG("Make some operations by ensuring that the fixed points are not modified") 210 | 211 | for(unsigned int i = 0; i < Threads; ++i){ 212 | pool.push_back(std::thread([&tree, &fixed_points, i](){ 213 | thread_num = i; 214 | 215 | std::vector rand; 216 | 217 | std::mt19937_64 engine(time(0) + i); 218 | 219 | //Note: The max() value cannot be handled by all data structure 220 | std::uniform_int_distribution distribution(0, std::numeric_limits::max() - 1); 221 | auto generator = std::bind(distribution, engine); 222 | 223 | std::uniform_int_distribution operationDistribution(0, 99); 224 | auto operationGenerator = std::bind(operationDistribution, engine); 225 | 226 | for(int n = 0; n < 10000; ++n){ 227 | auto value = generator(); 228 | 229 | if(operationGenerator() < 33){ 230 | if(std::find(fixed_points.begin(), fixed_points.end(), value) == fixed_points.end()){ 231 | tree.remove(value); 232 | } 233 | } else { 234 | tree.add(value); 235 | 236 | if(std::find(fixed_points.begin(), fixed_points.end(), value) == fixed_points.end()){ 237 | rand.push_back(value); 238 | } 239 | } 240 | 241 | assert(tree.contains(fixed_points[i])); 242 | } 243 | 244 | for(auto& value : rand){ 245 | tree.remove(value); 246 | } 247 | })); 248 | } 249 | 250 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 251 | 252 | for_each(fixed_points.begin(), fixed_points.end(), [&tree](int value){tree.remove(value);}); 253 | 254 | std::cout << "Test with " << Threads << " threads passed succesfully" << std::endl; 255 | } 256 | 257 | /*! 258 | * Launch all the tests on the given type. 259 | * \param type The type of the tree. 260 | * \param the The name of the tree. 261 | */ 262 | #define TEST(type, name) \ 263 | std::cout << "Test with 1 threads" << std::endl;\ 264 | testST>(name);\ 265 | std::cout << "Test multi-threaded (with " << MT_N << " elements) " << name << std::endl;\ 266 | testMT, 2>();\ 267 | testMT, 3>();\ 268 | testMT, 4>();\ 269 | testMT, 6>();\ 270 | testMT, 8>();\ 271 | testMT, 12>();\ 272 | testMT, 16>();\ 273 | testMT, 32>(); 274 | 275 | /*! 276 | * Test all the different versions. 277 | */ 278 | void test(){ 279 | std::cout << "Tests the different versions" << std::endl; 280 | 281 | //TEST(skiplist::SkipList, "SkipList") 282 | TEST(nbbst::NBBST, "Non-Blocking Binary Search Tree") 283 | //TEST(avltree::AVLTree, "Optimistic AVL Tree") 284 | //TEST(lfmst::MultiwaySearchTree, "Lock Free Multiway Search Tree"); 285 | //TEST(cbtree::CBTree, "Counter Based Tree"); 286 | } 287 | -------------------------------------------------------------------------------- /src/genzipf.cpp: -------------------------------------------------------------------------------- 1 | //==================================================== file = genzipf.c ===== 2 | //= Program to generate Zipf (power law) distributed random variables = 3 | //=========================================================================== 4 | //= Notes: 1) Writes to a user specified output file = 5 | //= 2) Generates user specified number of values = 6 | //= 3) Run times is same as an empirical distribution generator = 7 | //= 4) Implements p(i) = C/i^alpha for i = 1 to N where C is the = 8 | //= normalization constant (i.e., sum of p(i) = 1). = 9 | //=-------------------------------------------------------------------------= 10 | //= Example user input: = 11 | //= = 12 | //= ---------------------------------------- genzipf.c ----- = 13 | //= - Program to generate Zipf random variables - = 14 | //= -------------------------------------------------------- = 15 | //= Output file name ===================================> output.dat = 16 | //= Random number seed =================================> 1 = 17 | //= Alpha vlaue ========================================> 1.0 = 18 | //= N value ============================================> 1000 = 19 | //= Number of values to generate =======================> 5 = 20 | //= -------------------------------------------------------- = 21 | //= - Generating samples to file - = 22 | //= -------------------------------------------------------- = 23 | //= -------------------------------------------------------- = 24 | //= - Done! = 25 | //= -------------------------------------------------------- = 26 | //=-------------------------------------------------------------------------= 27 | //= Example output file ("output.dat" for above): = 28 | //= = 29 | //= 1 = 30 | //= 1 = 31 | //= 161 = 32 | //= 17 = 33 | //= 30 = 34 | //=-------------------------------------------------------------------------= 35 | //= Build: bcc32 genzipf.c = 36 | //=-------------------------------------------------------------------------= 37 | //= Execute: genzipf = 38 | //=-------------------------------------------------------------------------= 39 | //= Author: Kenneth J. Christensen = 40 | //= University of South Florida = 41 | //= WWW: http://www.csee.usf.edu/~christen = 42 | //= Email: christen@csee.usf.edu = 43 | //=-------------------------------------------------------------------------= 44 | //= History: KJC (11/16/03) - Genesis (from genexp.c) = 45 | //=========================================================================== 46 | 47 | #include // Needed for assert() macro 48 | #include // Needed for printf() 49 | #include // Needed for exit() and ato*() 50 | #include 51 | #include // Needed for pow() 52 | #include 53 | 54 | #include 55 | #include 56 | 57 | int zipf(int n); // Returns a Zipf random variable 58 | void init_zipf(int n); 59 | double rand_val(); // Jain's RNG 60 | void init_rand_val(int seed); 61 | 62 | double *powers; 63 | double *inverse_powers; 64 | double *c_powers; 65 | 66 | void slow_generate(const char* file, double alpha, int n, int num_value); 67 | 68 | int main(int /*argc*/, char*[] /*argv*/){ 69 | //Generate the histograms 70 | slow_generate("zipf/zipf-histo-02", 0.2, 1000, 50000); 71 | slow_generate("zipf/zipf-histo-08", 0.8, 1000, 50000); 72 | slow_generate("zipf/zipf-histo-12", 1.2, 1000, 50000); 73 | slow_generate("zipf/zipf-histo-18", 1.8, 1000, 50000); 74 | 75 | //Generate the data for the benchmarks 76 | slow_generate("zipf/zipf-00-2000", 0.0, 2000, 1000000); 77 | slow_generate("zipf/zipf-02-2000", 0.2, 2000, 1000000); 78 | slow_generate("zipf/zipf-04-2000", 0.4, 2000, 1000000); 79 | slow_generate("zipf/zipf-06-2000", 0.6, 2000, 1000000); 80 | slow_generate("zipf/zipf-08-2000", 0.8, 2000, 1000000); 81 | slow_generate("zipf/zipf-10-2000", 1.0, 2000, 1000000); 82 | slow_generate("zipf/zipf-12-2000", 1.2, 2000, 1000000); 83 | slow_generate("zipf/zipf-14-2000", 1.4, 2000, 1000000); 84 | slow_generate("zipf/zipf-16-2000", 1.6, 2000, 1000000); 85 | slow_generate("zipf/zipf-18-2000", 1.8, 2000, 1000000); 86 | slow_generate("zipf/zipf-20-2000", 2.0, 2000, 1000000); 87 | 88 | slow_generate("zipf/zipf-00-20000", 0.0, 20000, 1000000); 89 | slow_generate("zipf/zipf-02-20000", 0.2, 20000, 1000000); 90 | slow_generate("zipf/zipf-04-20000", 0.4, 20000, 1000000); 91 | slow_generate("zipf/zipf-06-20000", 0.6, 20000, 1000000); 92 | slow_generate("zipf/zipf-08-20000", 0.8, 20000, 1000000); 93 | slow_generate("zipf/zipf-10-20000", 1.0, 20000, 1000000); 94 | slow_generate("zipf/zipf-12-20000", 1.2, 20000, 1000000); 95 | slow_generate("zipf/zipf-14-20000", 1.4, 20000, 1000000); 96 | slow_generate("zipf/zipf-16-20000", 1.6, 20000, 1000000); 97 | slow_generate("zipf/zipf-18-20000", 1.8, 20000, 1000000); 98 | slow_generate("zipf/zipf-20-20000", 2.0, 20000, 1000000); 99 | 100 | slow_generate("zipf/zipf-00-200000", 0.0, 200000, 1000000); 101 | slow_generate("zipf/zipf-02-200000", 0.2, 200000, 1000000); 102 | slow_generate("zipf/zipf-04-200000", 0.4, 200000, 1000000); 103 | slow_generate("zipf/zipf-06-200000", 0.6, 200000, 1000000); 104 | slow_generate("zipf/zipf-08-200000", 0.8, 200000, 1000000); 105 | slow_generate("zipf/zipf-10-200000", 1.0, 200000, 1000000); 106 | slow_generate("zipf/zipf-12-200000", 1.2, 200000, 1000000); 107 | slow_generate("zipf/zipf-14-200000", 1.4, 200000, 1000000); 108 | slow_generate("zipf/zipf-16-200000", 1.6, 200000, 1000000); 109 | slow_generate("zipf/zipf-18-200000", 1.8, 200000, 1000000); 110 | slow_generate("zipf/zipf-20-200000", 2.0, 200000, 1000000); 111 | 112 | slow_generate("zipf/zipf-00-2000000", 0.0, 2000000, 1000000); 113 | slow_generate("zipf/zipf-02-2000000", 0.2, 2000000, 1000000); 114 | slow_generate("zipf/zipf-04-2000000", 0.4, 2000000, 1000000); 115 | slow_generate("zipf/zipf-06-2000000", 0.6, 2000000, 1000000); 116 | slow_generate("zipf/zipf-08-2000000", 0.8, 2000000, 1000000); 117 | slow_generate("zipf/zipf-10-2000000", 1.0, 2000000, 1000000); 118 | slow_generate("zipf/zipf-12-2000000", 1.2, 2000000, 1000000); 119 | slow_generate("zipf/zipf-14-2000000", 1.4, 2000000, 1000000); 120 | slow_generate("zipf/zipf-16-2000000", 1.6, 2000000, 1000000); 121 | slow_generate("zipf/zipf-18-2000000", 1.8, 2000000, 1000000); 122 | slow_generate("zipf/zipf-20-2000000", 2.0, 2000000, 1000000); 123 | 124 | return 0; 125 | } 126 | 127 | int slow_zipf(double alpha, int n); // Returns a Zipf random variable 128 | double slow_rand_val(); // Jain's RNG 129 | void slow_init(double alpha, int n); 130 | 131 | static int seed; 132 | static double c; 133 | 134 | void slow_generate(const char* file_name, double alpha, int n, int num_values){ 135 | std::cout << "Slow Generate " << num_values << " values in [0, " << n << "] with a skew of " << alpha << std::endl; 136 | 137 | srand(time(NULL)); 138 | seed = rand(); 139 | 140 | slow_init(alpha, n); 141 | 142 | FILE *fp; // File pointer to output file 143 | fp = fopen(file_name, "w"); 144 | if (fp == NULL){ 145 | printf("ERROR in creating output file (%s) \n", file_name); 146 | exit(1); 147 | } 148 | 149 | // Generate and output zipf random variables 150 | for (int i=0; i= z){ 180 | zipf_value = i; 181 | break; 182 | } 183 | } 184 | 185 | return zipf_value; 186 | } 187 | 188 | double slow_rand_val(){ 189 | const long a = 16807; // Multiplier 190 | const long m = 2147483647; // Modulus 191 | const long q = 127773; // m div a 192 | const long r = 2836; // m mod a 193 | long x_div_q; // x divided by q 194 | long x_mod_q; // x modulo q 195 | long x_new; // New x value 196 | 197 | // RNG using integer arithmetic 198 | x_div_q = seed / q; 199 | x_mod_q = seed % q; 200 | x_new = (a * x_mod_q) - (r * x_div_q); 201 | 202 | if (x_new > 0) 203 | seed = x_new; 204 | else 205 | seed = x_new + m; 206 | 207 | // Return a random value between 0.0 and 1.0 208 | return((double) seed / m); 209 | } 210 | -------------------------------------------------------------------------------- /include/HazardManager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HAZARD_MANAGER 2 | #define HAZARD_MANAGER 3 | 4 | #include 5 | 6 | //#define DEBUG //Indicates that the at() function is used for array accesses 7 | 8 | //Thread local id 9 | //Note: __thread is GCC specific 10 | extern __thread unsigned int thread_num; 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | /*! 18 | * A manager for Hazard Pointers manipulation. 19 | * \param Node The type of node to manage. 20 | * \param Threads The maximum number of threads. 21 | * \param Size The number of hazard pointers per thread. 22 | * \param Prefill The number of nodes to precreate in the queue. 23 | */ 24 | template 25 | class HazardManager { 26 | public: 27 | HazardManager(); 28 | ~HazardManager(); 29 | 30 | HazardManager(const HazardManager& rhs) = delete; 31 | HazardManager& operator=(const HazardManager& rhs) = delete; 32 | 33 | /*! 34 | * Release the node. 35 | */ 36 | void releaseNode(Node* node); 37 | 38 | /*! 39 | * \brief Release the node by checking first if it is not already in the queue. 40 | * This method can be slow depending on the number of nodes already released. 41 | * \param node The node to release. 42 | */ 43 | void safe_release_node(Node* node); 44 | 45 | /*! 46 | * Return a free node for the calling thread. 47 | * \return A free node 48 | */ 49 | Node* getFreeNode(); 50 | 51 | /*! 52 | * Publish a reference to the given Node using ith Hazard Pointer. 53 | * \param node The node to be published 54 | * \param i The index of the pointer to use. 55 | */ 56 | void publish(Node* node, unsigned int i); 57 | 58 | /*! 59 | * Release the ith reference of the calling thread. 60 | * \param i The reference index. 61 | */ 62 | void release(unsigned int i); 63 | 64 | /*! 65 | * Release all the hazard points of the calling thread. 66 | */ 67 | void releaseAll(); 68 | 69 | /*! 70 | * Return a reference to the internal free queue of the given thread. 71 | * \return A reference to the free queue of the given thread. 72 | */ 73 | std::list& direct_free(unsigned int t); 74 | 75 | /*! 76 | * Return a reference to the internal local queue of the given thread. 77 | * \return A reference to the local queue of the given thread. 78 | */ 79 | std::list& direct_local(unsigned int t); 80 | 81 | private: 82 | std::array, Threads> Pointers; 83 | std::array, Threads> LocalQueues; 84 | std::array, Threads> FreeQueues; 85 | 86 | bool isReferenced(Node* node); 87 | 88 | /* Verify the template parameters */ 89 | static_assert(Threads > 0, "The number of threads must be greater than 0"); 90 | static_assert(Size > 0, "The number of hazard pointers must greater than 0"); 91 | }; 92 | 93 | template 94 | HazardManager::HazardManager(){ 95 | for(unsigned int tid = 0; tid < Threads; ++tid){ 96 | for(unsigned int j = 0; j < Size; ++j){ 97 | #ifdef DEBUG 98 | Pointers.at(tid).at(j) = nullptr; 99 | #else 100 | Pointers[tid][j] = nullptr; 101 | #endif 102 | } 103 | 104 | if(Prefill > 0){ 105 | for(unsigned int i = 0; i < Prefill; i++){ 106 | #ifdef DEBUG 107 | FreeQueues.at(tid).push_back(new Node()); 108 | #else 109 | FreeQueues[tid].push_back(new Node()); 110 | #endif 111 | } 112 | } 113 | } 114 | } 115 | 116 | template 117 | HazardManager::~HazardManager(){ 118 | for(unsigned int tid = 0; tid < Threads; ++tid){ 119 | //No need to delete Hazard Pointers because each thread need to release its published references 120 | 121 | #ifdef DEBUG 122 | while(!LocalQueues.at(tid).empty()){ 123 | delete LocalQueues.at(tid).front(); 124 | LocalQueues.at(tid).pop_front(); 125 | } 126 | 127 | while(!FreeQueues.at(tid).empty()){ 128 | delete FreeQueues.at(tid).front(); 129 | FreeQueues.at(tid).pop_front(); 130 | } 131 | #else 132 | while(!LocalQueues[tid].empty()){ 133 | delete LocalQueues[tid].front(); 134 | LocalQueues[tid].pop_front(); 135 | } 136 | 137 | while(!FreeQueues[tid].empty()){ 138 | delete FreeQueues[tid].front(); 139 | FreeQueues[tid].pop_front(); 140 | } 141 | #endif 142 | } 143 | } 144 | 145 | template 146 | std::list& HazardManager::direct_free(unsigned int t){ 147 | #ifdef DEBUG 148 | return FreeQueues.at(t); 149 | #else 150 | return FreeQueues[t]; 151 | #endif 152 | } 153 | 154 | template 155 | std::list& HazardManager::direct_local(unsigned int t){ 156 | #ifdef DEBUG 157 | return LocalQueues.at(t); 158 | #else 159 | return LocalQueues[t]; 160 | #endif 161 | } 162 | 163 | template 164 | void HazardManager::safe_release_node(Node* node){ 165 | //If the node is null, we have nothing to do 166 | if(node){ 167 | if(std::find(LocalQueues.at(thread_num).begin(), LocalQueues.at(thread_num).end(), node) != LocalQueues.at(thread_num).end()){ 168 | return; 169 | } 170 | 171 | //Add the node to the localqueue 172 | LocalQueues.at(thread_num).push_back(node); 173 | } 174 | } 175 | 176 | template 177 | void HazardManager::releaseNode(Node* node){ 178 | //If the node is null, we have nothing to do 179 | if(node){ 180 | #ifdef DEBUG 181 | //Add the node to the localqueue 182 | LocalQueues.at(thread_num).push_back(node); 183 | #else 184 | //Add the node to the localqueue 185 | LocalQueues[thread_num].push_back(node); 186 | #endif 187 | } 188 | } 189 | 190 | template 191 | Node* HazardManager::getFreeNode(){ 192 | int tid = thread_num; 193 | 194 | #ifdef DEBUG 195 | //First, try to get a free node from the free queue 196 | if(!FreeQueues.at(tid).empty()){ 197 | Node* free = FreeQueues.at(tid).front(); 198 | FreeQueues.at(tid).pop_front(); 199 | 200 | return free; 201 | } 202 | 203 | //If there are enough local nodes, move then to the free queue 204 | if(LocalQueues.at(tid).size() > (Size + 1) * Threads){ 205 | typename std::list::iterator it = LocalQueues.at(tid).begin(); 206 | typename std::list::iterator end = LocalQueues.at(tid).end(); 207 | 208 | while(it != end){ 209 | if(!isReferenced(*it)){ 210 | FreeQueues.at(tid).push_back(*it); 211 | 212 | it = LocalQueues.at(tid).erase(it); 213 | } 214 | else { 215 | ++it; 216 | } 217 | } 218 | 219 | Node* free = FreeQueues.at(tid).front(); 220 | FreeQueues.at(tid).pop_front(); 221 | 222 | return free; 223 | } 224 | #else 225 | //First, try to get a free node from the free queue 226 | if(!FreeQueues[tid].empty()){ 227 | Node* free = FreeQueues[tid].front(); 228 | FreeQueues[tid].pop_front(); 229 | 230 | return free; 231 | } 232 | 233 | //If there are enough local nodes, move then to the free queue 234 | if(LocalQueues[tid].size() > (Size + 1) * Threads){ 235 | typename std::list::iterator it = LocalQueues[tid].begin(); 236 | typename std::list::iterator end = LocalQueues[tid].end(); 237 | 238 | while(it != end){ 239 | if(!isReferenced(*it)){ 240 | FreeQueues[tid].push_back(*it); 241 | 242 | it = LocalQueues[tid].erase(it); 243 | } 244 | else { 245 | ++it; 246 | } 247 | } 248 | 249 | Node* free = FreeQueues[tid].front(); 250 | FreeQueues[tid].pop_front(); 251 | 252 | return free; 253 | } 254 | #endif 255 | 256 | //There was no way to get a free node, allocate a new one 257 | return new Node(); 258 | } 259 | 260 | template 261 | bool HazardManager::isReferenced(Node* node){ 262 | #ifdef DEBUG 263 | for(unsigned int tid = 0; tid < Threads; ++tid){ 264 | for(unsigned int i = 0; i < Size; ++i){ 265 | if(Pointers.at(tid).at(i) == node){ 266 | return true; 267 | } 268 | } 269 | } 270 | #else 271 | for(unsigned int tid = 0; tid < Threads; ++tid){ 272 | for(unsigned int i = 0; i < Size; ++i){ 273 | if(Pointers[tid][i] == node){ 274 | return true; 275 | } 276 | } 277 | } 278 | #endif 279 | 280 | return false; 281 | } 282 | 283 | template 284 | void HazardManager::publish(Node* node, unsigned int i){ 285 | #ifdef DEBUG 286 | Pointers.at(thread_num).at(i) = node; 287 | #else 288 | Pointers[thread_num][i] = node; 289 | #endif 290 | } 291 | 292 | template 293 | void HazardManager::release(unsigned int i){ 294 | #ifdef DEBUG 295 | Pointers.at(thread_num).at(i) = nullptr; 296 | #else 297 | Pointers[thread_num][i] = nullptr; 298 | #endif 299 | } 300 | 301 | template 302 | void HazardManager::releaseAll(){ 303 | #ifdef DEBUG 304 | for(unsigned int i = 0; i < Size; ++i){ 305 | Pointers.at(thread_num).at(i) = nullptr; 306 | } 307 | #else 308 | for(unsigned int i = 0; i < Size; ++i){ 309 | Pointers[thread_num][i] = nullptr; 310 | } 311 | #endif 312 | } 313 | 314 | #endif 315 | -------------------------------------------------------------------------------- /include/nbbst/NBBST.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NBBST_TREE 2 | #define NBBST_TREE 3 | 4 | #include 5 | 6 | #include "hash.hpp" 7 | #include "Utils.hpp" 8 | 9 | namespace nbbst { 10 | 11 | enum UpdateState { 12 | CLEAN = 0, 13 | DFLAG = 1, 14 | IFLAG = 2, 15 | MARK = 3 16 | }; 17 | 18 | struct Node; 19 | 20 | struct Info; 21 | typedef Info* Update; 22 | 23 | struct Info { 24 | Node* gp; //Internal 25 | Node* p; //Internal 26 | Node* newInternal; //Internal 27 | Node* l; //Leaf 28 | Update pupdate; 29 | 30 | Info() : gp(nullptr), p(nullptr), newInternal(nullptr), l(nullptr), pupdate(nullptr) {} 31 | }; 32 | 33 | typedef Info* Update; 34 | 35 | inline UpdateState getState(Update update){ 36 | return static_cast(reinterpret_cast(update) & 3l); 37 | } 38 | 39 | inline Update Unmark(Update info){ 40 | return reinterpret_cast(reinterpret_cast(info) & (~0l - 3)); 41 | } 42 | 43 | inline Update Mark(Update info, UpdateState state){ 44 | return reinterpret_cast((reinterpret_cast(info) & (~0l - 3)) | static_cast(state)); 45 | } 46 | 47 | struct Node { 48 | bool internal; 49 | int key; 50 | 51 | Update update; 52 | Node* left; 53 | Node* right; 54 | 55 | Node() : internal(internal), key(key), update(nullptr), left(nullptr), right(nullptr) {}; 56 | }; 57 | 58 | struct SearchResult { 59 | Node* gp; //Internal 60 | Node* p; //Internal 61 | Node* l; //Leaf 62 | Update pupdate; 63 | Update gpupdate; 64 | 65 | SearchResult() : gp(nullptr), p(nullptr), l(nullptr), pupdate(nullptr), gpupdate(nullptr) {} 66 | }; 67 | 68 | template 69 | class NBBST { 70 | public: 71 | NBBST(); 72 | ~NBBST(); 73 | 74 | bool contains(T value); 75 | bool add(T value); 76 | bool remove(T value); 77 | 78 | private: 79 | void Search(int key, SearchResult* result); 80 | void HelpInsert(Info* op); 81 | bool HelpDelete(Info* op); 82 | void HelpMarked(Info* op); 83 | void Help(Update u); 84 | void CASChild(Node* parent, Node* old, Node* newNode); 85 | 86 | /* Allocate stuff from the hazard manager */ 87 | Node* newInternal(int key); 88 | Node* newLeaf(int key); 89 | Info* newIInfo(Node* p, Node* newInternal, Node* l); 90 | Info* newDInfo(Node* gp, Node* p, Node* l, Update pupdate); 91 | 92 | /* To remove properly a node */ 93 | void releaseNode(Node* node); 94 | 95 | Node* root; 96 | 97 | HazardManager nodes; 98 | HazardManager infos; 99 | }; 100 | 101 | template 102 | NBBST::NBBST(){ 103 | root = newInternal(std::numeric_limits::max()); 104 | root->update = Mark(nullptr, CLEAN); 105 | 106 | root->left = newLeaf(std::numeric_limits::min()); 107 | root->right = newLeaf(std::numeric_limits::max()); 108 | } 109 | 110 | template 111 | NBBST::~NBBST(){ 112 | //Remove the three nodes created in the constructor 113 | releaseNode(root->left); 114 | releaseNode(root->right); 115 | releaseNode(root); 116 | } 117 | 118 | template 119 | Node* NBBST::newInternal(int key){ 120 | Node* node = nodes.getFreeNode(); 121 | 122 | node->internal = true; 123 | node->key = key; 124 | 125 | return node; 126 | } 127 | 128 | template 129 | Node* NBBST::newLeaf(int key){ 130 | Node* node = nodes.getFreeNode(); 131 | 132 | node->internal = false; 133 | node->key = key; 134 | 135 | return node; 136 | } 137 | 138 | template 139 | Info* NBBST::newIInfo(Node* p, Node* newInternal, Node* l){ 140 | Info* info = infos.getFreeNode(); 141 | 142 | info->p = p; 143 | info->newInternal = newInternal; 144 | info->l = l; 145 | 146 | return info; 147 | } 148 | 149 | template 150 | Info* NBBST::newDInfo(Node* gp, Node* p, Node* l, Update pupdate){ 151 | Info* info = infos.getFreeNode(); 152 | 153 | info->gp = gp; 154 | info->p = p; 155 | info->l = l; 156 | info->pupdate = pupdate; 157 | 158 | return info; 159 | } 160 | 161 | template 162 | void NBBST::releaseNode(Node* node){ 163 | if(node){ 164 | if(node->update){ 165 | infos.releaseNode(Unmark(node->update)); 166 | } 167 | 168 | nodes.releaseNode(node); 169 | } 170 | } 171 | 172 | template 173 | void NBBST::Search(int key, SearchResult* result){ 174 | Node* l = root; 175 | 176 | while(l->internal){ 177 | result->gp = result->p; 178 | result->p = l; 179 | result->gpupdate = result->pupdate; 180 | result->pupdate = result->p->update; 181 | 182 | if(key < l->key){ 183 | l = result->p->left; 184 | } else { 185 | l = result->p->right; 186 | } 187 | } 188 | 189 | result->l = l; 190 | } 191 | 192 | template 193 | bool NBBST::contains(T value){ 194 | int key = hash(value); 195 | 196 | SearchResult result; 197 | Search(key, &result); 198 | 199 | return result.l->key == key; 200 | } 201 | 202 | template 203 | bool NBBST::add(T value){ 204 | int key = hash(value); 205 | 206 | Node* newNode = newLeaf(key); 207 | 208 | SearchResult search; 209 | 210 | while(true){ 211 | Search(key, &search); 212 | 213 | nodes.publish(search.l, 0); 214 | 215 | infos.publish(search.p->update, 0); 216 | infos.publish(search.pupdate, 1); 217 | 218 | if(search.l->key == key){ 219 | nodes.releaseNode(newNode); 220 | nodes.releaseAll(); 221 | 222 | infos.releaseAll(); 223 | 224 | return false; //Key already in the set 225 | } 226 | 227 | if(getState(search.pupdate) != CLEAN){ 228 | Help(search.pupdate); 229 | } else { 230 | Node* newSibling = newLeaf(search.l->key); 231 | Node* newInt = newInternal(std::max(key, search.l->key)); 232 | newInt->update = Mark(nullptr, CLEAN); 233 | 234 | //Put the smaller child on the left 235 | if(newNode->key <= newSibling->key){ 236 | newInt->left = newNode; 237 | newInt->right = newSibling; 238 | } else { 239 | newInt->left = newSibling; 240 | newInt->right = newNode; 241 | } 242 | 243 | Info* op = newIInfo(search.p, newInt, search.l); 244 | infos.publish(op, 2); 245 | 246 | Update result = search.p->update; 247 | if(CASPTR(&search.p->update, search.pupdate, Mark(op, IFLAG))){ 248 | HelpInsert(op); 249 | 250 | if(search.pupdate){ 251 | infos.releaseNode(Unmark(search.pupdate)); 252 | } 253 | 254 | nodes.releaseAll(); 255 | infos.releaseAll(); 256 | 257 | return true; 258 | } else { 259 | nodes.releaseNode(newInt); 260 | nodes.releaseNode(newSibling); 261 | nodes.releaseAll(); 262 | 263 | infos.releaseNode(op); 264 | infos.releaseAll(); 265 | 266 | Help(result); 267 | } 268 | } 269 | } 270 | } 271 | 272 | template 273 | bool NBBST::remove(T value){ 274 | int key = hash(value); 275 | 276 | SearchResult search; 277 | 278 | while(true){ 279 | Search(key, &search); 280 | nodes.publish(search.l, 0); 281 | 282 | if(search.l->key != key){ 283 | return false; 284 | } 285 | 286 | if(getState(search.gpupdate) != CLEAN){ 287 | Help(search.gpupdate); 288 | } else if(getState(search.pupdate) != CLEAN){ 289 | Help(search.pupdate); 290 | } else { 291 | infos.publish(search.gp->update, 0); 292 | infos.publish(search.gpupdate, 1); 293 | 294 | Info* op = newDInfo(search.gp, search.p, search.l, search.pupdate); 295 | infos.publish(op, 2); 296 | 297 | Update result = search.gp->update; 298 | if(CASPTR(&search.gp->update, search.gpupdate, Mark(op, DFLAG))){ 299 | if(search.gpupdate){ 300 | infos.releaseNode(Unmark(search.gpupdate)); 301 | } 302 | 303 | infos.releaseAll(); 304 | 305 | if(HelpDelete(op)){ 306 | nodes.releaseAll(); 307 | 308 | return true; 309 | } 310 | } else { 311 | infos.releaseNode(op); 312 | infos.releaseAll(); 313 | 314 | Help(result); 315 | } 316 | } 317 | 318 | nodes.releaseAll(); 319 | } 320 | } 321 | 322 | template 323 | void NBBST::Help(Update u){ 324 | if(getState(u) == IFLAG){ 325 | HelpInsert(Unmark(u)); 326 | } else if(getState(u) == MARK){ 327 | HelpMarked(Unmark(u)); 328 | } else if(getState(u) == DFLAG){ 329 | HelpDelete(Unmark(u)); 330 | } 331 | } 332 | 333 | template 334 | void NBBST::HelpInsert(Info* op){ 335 | infos.publish(op, 0); 336 | infos.publish(op->p->update, 1); 337 | 338 | CASChild(op->p, op->l, op->newInternal); 339 | CASPTR(&op->p->update, Mark(op, IFLAG), Mark(op, CLEAN)); 340 | 341 | infos.releaseAll(); 342 | } 343 | 344 | template 345 | bool NBBST::HelpDelete(Info* op){ 346 | infos.publish(op->p->update, 0); 347 | infos.publish(op->pupdate, 1); 348 | infos.publish(op, 2); 349 | 350 | Update result = op->p->update; 351 | 352 | //If we succeed 353 | if(CASPTR(&op->p->update, op->pupdate, Mark(op, MARK))){ 354 | if(op->pupdate){ 355 | infos.releaseNode(Unmark(op->pupdate)); 356 | } 357 | 358 | nodes.releaseNode(op->l); 359 | HelpMarked(Unmark(op)); 360 | infos.releaseAll(); 361 | 362 | return true; 363 | } 364 | //if another has succeeded for us 365 | else if(getState(op->p->update) == MARK && Unmark(op->p->update) == Unmark(op)){ 366 | HelpMarked(Unmark(op)); 367 | infos.releaseAll(); 368 | return true; 369 | } else { 370 | Help(result); 371 | 372 | infos.publish(op->gp->update, 0); 373 | infos.publish(op, 1); 374 | CASPTR(&op->gp->update, Mark(op, DFLAG), Mark(op, CLEAN)); 375 | infos.releaseAll(); 376 | 377 | return false; 378 | } 379 | } 380 | 381 | template 382 | void NBBST::HelpMarked(Info* op){ 383 | Node* other; 384 | 385 | if(op->p->right == op->l){ 386 | other = op->p->left; 387 | } else { 388 | other = op->p->right; 389 | } 390 | 391 | CASChild(op->gp, op->p, other); 392 | 393 | infos.publish(op->gp->update, 0); 394 | infos.publish(op, 1); 395 | CASPTR(&op->gp->update, Mark(op, DFLAG), Mark(op, CLEAN)); 396 | infos.releaseAll(); 397 | } 398 | 399 | template 400 | void NBBST::CASChild(Node* parent, Node* old, Node* newNode){ 401 | nodes.publish(old, 0); 402 | nodes.publish(newNode, 1); 403 | 404 | if(newNode->key < parent->key){ 405 | nodes.publish(parent->left, 2); 406 | if(CASPTR(&parent->left, old, newNode)){ 407 | if(old){ 408 | nodes.releaseNode(old); 409 | } 410 | } 411 | } else { 412 | nodes.publish(parent->right, 2); 413 | if(CASPTR(&parent->right, old, newNode)){ 414 | if(old){ 415 | nodes.releaseNode(old); 416 | } 417 | } 418 | } 419 | 420 | nodes.releaseAll(); 421 | } 422 | 423 | } //end of nbbst 424 | 425 | #endif 426 | -------------------------------------------------------------------------------- /include/avltree/AVLTree.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AVL_TREE_TREE 2 | #define AVL_TREE_TREE 3 | 4 | #include 5 | 6 | #include "hash.hpp" 7 | #include "HazardManager.hpp" 8 | 9 | namespace avltree { 10 | 11 | typedef std::lock_guard scoped_lock; 12 | 13 | static int SpinCount = 100; 14 | 15 | static long beginChange(long ovl) { return ovl | 1; } 16 | static long endChange(long ovl) { return (ovl | 3) + 1; } 17 | 18 | static bool isShrinking(long ovl) { return (ovl & 1) != 0; } 19 | static bool isUnlinked(long ovl) { return (ovl & 2) != 0; } 20 | static bool isShrinkingOrUnlinked(long ovl) { return (ovl & 3) != 0L; } 21 | 22 | static long UnlinkedOVL = 2; 23 | 24 | /* Conditions on nodes */ 25 | static const int UnlinkRequired = -1; 26 | static const int RebalanceRequired = -2; 27 | static const int NothingRequired = -3; 28 | 29 | enum Function { 30 | UpdateIfPresent, 31 | UpdateIfAbsent 32 | }; 33 | 34 | struct Node { 35 | int height; 36 | int key; 37 | long version; 38 | bool value; 39 | Node* parent; 40 | Node* left; 41 | Node* right; 42 | 43 | std::mutex lock; 44 | 45 | Node* child(int direction){ 46 | if(direction > 0){ 47 | return right; 48 | } else if(direction < 0){ 49 | return left; 50 | } 51 | 52 | assert(false); 53 | } 54 | 55 | //Should only be called with lock on Node 56 | void setChild(int direction, Node* child){ 57 | if(direction > 0){ 58 | right = child; 59 | } else if(direction < 0){ 60 | left = child; 61 | } 62 | } 63 | }; 64 | 65 | enum Result { 66 | FOUND, 67 | NOT_FOUND, 68 | RETRY 69 | }; 70 | 71 | template 72 | class AVLTree { 73 | public: 74 | AVLTree(); 75 | ~AVLTree(); 76 | 77 | bool contains(T value); 78 | bool add(T value); 79 | bool remove(T value); 80 | 81 | private: 82 | /* Allocate new nodes */ 83 | Node* newNode(int key); 84 | Node* newNode(int height, int key, long version, bool value, Node* parent, Node* left, Node* right); 85 | 86 | //Search 87 | Result attemptGet(int key, Node* node, int dir, long nodeV); 88 | 89 | /* Update stuff */ 90 | Result updateUnderRoot(int key, Function func, bool expected, bool newValue, Node* holder); 91 | bool attemptInsertIntoEmpty(int key, bool value, Node* holder); 92 | Result attemptUpdate(int key, Function func, bool expected, bool newValue, Node* parent, Node* node, long nodeOVL); 93 | Result attemptNodeUpdate(Function func, bool expected, bool newValue, Node* parent, Node* node); 94 | bool attemptUnlink_nl(Node* parent, Node* node); 95 | 96 | /* To wait during shrinking */ 97 | void waitUntilNotChanging(Node* node); 98 | 99 | /* Rebalancing stuff */ 100 | void fixHeightAndRebalance(Node* node); 101 | Node* rebalance_nl(Node* nParent, Node* n); 102 | Node* rebalanceToRight_nl(Node* nParent, Node* n, Node* nL, int hR0); 103 | Node* rebalanceToLeft_nl(Node* nParent, Node* n, Node* nR, int hL0); 104 | 105 | /* Rotation stuff */ 106 | Node* rotateLeftOverRight_nl(Node* nParent, Node* n, int hL, Node* nR, Node* nRL, int hRR, int hRLR); 107 | Node* rotateRightOverLeft_nl(Node* nParent, Node* n, Node* nL, int hR, int hLL, Node* nLR, int hLRL); 108 | Node* rotateLeft_nl(Node* nParent, Node* n, int hL, Node* nR, Node* nRL, int hRL, int hRR); 109 | Node* rotateRight_nl(Node* nParent, Node* n, Node* nL, int hR, int hLL, Node* nLR, int hLR); 110 | 111 | void publish(Node* ref); 112 | void releaseAll(); 113 | 114 | Node* rootHolder; 115 | 116 | HazardManager hazard; 117 | 118 | unsigned int Current[Threads]; 119 | }; 120 | 121 | static Node* fixHeight_nl(Node* n); 122 | static int height(Node* node); 123 | static int nodeCondition(Node* node); 124 | 125 | template 126 | AVLTree::AVLTree(){ 127 | rootHolder = newNode(std::numeric_limits::min()); 128 | 129 | for(unsigned int i = 0; i < Threads; ++i){ 130 | Current[i] = 0; 131 | } 132 | } 133 | 134 | template 135 | AVLTree::~AVLTree(){ 136 | hazard.releaseNode(rootHolder); 137 | } 138 | 139 | template 140 | void AVLTree::publish(Node* ref){ 141 | hazard.publish(ref, Current[thread_num]); 142 | ++Current[thread_num]; 143 | } 144 | 145 | template 146 | void AVLTree::releaseAll(){ 147 | for(unsigned int i = 0; i < Current[thread_num]; ++i){ 148 | hazard.release(i); 149 | } 150 | 151 | Current[thread_num] = 0; 152 | } 153 | 154 | template 155 | Node* AVLTree::newNode(int key){ 156 | return newNode(1, key, 0L, false, nullptr, nullptr, nullptr); 157 | } 158 | 159 | template 160 | Node* AVLTree::newNode(int height, int key, long version, bool value, Node* parent, Node* left, Node* right){ 161 | Node* node = hazard.getFreeNode(); 162 | 163 | node->height = height; 164 | node->key = key; 165 | node->version = version; 166 | node->value = value; 167 | node->parent = parent; 168 | node->left = left; 169 | node->right = right; 170 | 171 | return node; 172 | } 173 | 174 | template 175 | bool AVLTree::contains(T value){ 176 | int key = hash(value); 177 | 178 | while(true){ 179 | Node* right = rootHolder->right; 180 | 181 | if(!right){ 182 | return false; 183 | } else { 184 | int rightCmp = key - right->key; 185 | if(rightCmp == 0){ 186 | return right->value; 187 | } 188 | 189 | int ovl = right->version; 190 | if(isShrinkingOrUnlinked(ovl)){ 191 | waitUntilNotChanging(right); 192 | } else if(right == rootHolder->right){ 193 | Result vo = attemptGet(key, right, rightCmp, ovl); 194 | if(vo != RETRY){ 195 | return vo == FOUND; 196 | } 197 | } 198 | } 199 | } 200 | } 201 | 202 | template 203 | Result AVLTree::attemptGet(int key, Node* node, int dir, long nodeV){ 204 | while(true){ 205 | Node* child = node->child(dir); 206 | 207 | if(!child){ 208 | if(node->version != nodeV){ 209 | return RETRY; 210 | } 211 | 212 | return NOT_FOUND; 213 | } else { 214 | int childCmp = key - child->key; 215 | if(childCmp == 0){ 216 | return child->value ? FOUND : NOT_FOUND;//Verify that it's a value node 217 | } 218 | 219 | long childOVL = child->version; 220 | if(isShrinkingOrUnlinked(childOVL)){ 221 | waitUntilNotChanging(child); 222 | 223 | if(node->version != nodeV){ 224 | return RETRY; 225 | } 226 | } else if(child != node->child(dir)){ 227 | if(node->version != nodeV){ 228 | return RETRY; 229 | } 230 | } else { 231 | if(node->version != nodeV){ 232 | return RETRY; 233 | } 234 | 235 | Result result = attemptGet(key, child, childCmp, childOVL); 236 | if(result != RETRY){ 237 | return result; 238 | } 239 | } 240 | } 241 | } 242 | } 243 | 244 | inline bool shouldUpdate(Function func, bool prev, bool/* expected*/){ 245 | return func == UpdateIfAbsent ? !prev : prev; 246 | } 247 | 248 | inline Result updateResult(Function func, bool/* prev*/){ 249 | return func == UpdateIfAbsent ? NOT_FOUND : FOUND; 250 | } 251 | 252 | inline Result noUpdateResult(Function func, bool/* prev*/){ 253 | return func == UpdateIfAbsent ? FOUND : NOT_FOUND; 254 | } 255 | 256 | template 257 | bool AVLTree::add(T value){ 258 | return updateUnderRoot(hash(value), UpdateIfAbsent, false, true, rootHolder) == NOT_FOUND; 259 | } 260 | 261 | template 262 | bool AVLTree::remove(T value){ 263 | return updateUnderRoot(hash(value), UpdateIfPresent, true, false, rootHolder) == FOUND; 264 | } 265 | 266 | template 267 | Result AVLTree::updateUnderRoot(int key, Function func, bool expected, bool newValue, Node* holder){ 268 | while(true){ 269 | Node* right = holder->right; 270 | 271 | if(!right){ 272 | if(!shouldUpdate(func, false, expected)){ 273 | return noUpdateResult(func, false); 274 | } 275 | 276 | if(!newValue || attemptInsertIntoEmpty(key, newValue, holder)){ 277 | return updateResult(func, false); 278 | } 279 | } else { 280 | long ovl = right->version; 281 | 282 | if(isShrinkingOrUnlinked(ovl)){ 283 | waitUntilNotChanging(right); 284 | } else if(right == holder->right){ 285 | Result vo = attemptUpdate(key, func, expected, newValue, holder, right, ovl); 286 | if(vo != RETRY){ 287 | return vo; 288 | } 289 | } 290 | } 291 | } 292 | } 293 | 294 | template 295 | bool AVLTree::attemptInsertIntoEmpty(int key, bool value, Node* holder){ 296 | publish(holder); 297 | scoped_lock lock(holder->lock); 298 | 299 | if(!holder->right){ 300 | holder->right = newNode(1, key, 0, value, holder, nullptr, nullptr); 301 | holder->height = 2; 302 | releaseAll(); 303 | return true; 304 | } else { 305 | releaseAll(); 306 | return false; 307 | } 308 | } 309 | 310 | template 311 | Result AVLTree::attemptUpdate(int key, Function func, bool expected, bool newValue, Node* parent, Node* node, long nodeOVL){ 312 | int cmp = key - node->key; 313 | if(cmp == 0){ 314 | return attemptNodeUpdate(func, expected, newValue, parent, node); 315 | } 316 | 317 | while(true){ 318 | Node* child = node->child(cmp); 319 | 320 | if(node->version != nodeOVL){ 321 | return RETRY; 322 | } 323 | 324 | if(!child){ 325 | if(!newValue){ 326 | return NOT_FOUND; 327 | } else { 328 | bool success; 329 | Node* damaged; 330 | 331 | { 332 | publish(node); 333 | scoped_lock lock(node->lock); 334 | 335 | if(node->version != nodeOVL){ 336 | releaseAll(); 337 | return RETRY; 338 | } 339 | 340 | if(node->child(cmp)){ 341 | success = false; 342 | damaged = nullptr; 343 | } else { 344 | if(!shouldUpdate(func, false, expected)){ 345 | releaseAll(); 346 | return noUpdateResult(func, false); 347 | } 348 | 349 | Node* newChild = newNode(1, key, 0, true, node, nullptr, nullptr); 350 | node->setChild(cmp, newChild); 351 | 352 | success = true; 353 | damaged = fixHeight_nl(node); 354 | } 355 | 356 | releaseAll(); 357 | } 358 | 359 | if(success){ 360 | fixHeightAndRebalance(damaged); 361 | return updateResult(func, nullptr); 362 | } 363 | } 364 | } else { 365 | long childOVL = child->version; 366 | 367 | if(isShrinkingOrUnlinked(childOVL)){ 368 | waitUntilNotChanging(child); 369 | } else if(child != node->child(cmp)){ 370 | //RETRY 371 | } else { 372 | if(node->version != nodeOVL){ 373 | return RETRY; 374 | } 375 | 376 | Result vo = attemptUpdate(key, func, expected, newValue, node, child, childOVL); 377 | if(vo != RETRY){ 378 | return vo; 379 | } 380 | } 381 | } 382 | } 383 | } 384 | 385 | template 386 | Result AVLTree::attemptNodeUpdate(Function func, bool expected, bool newValue, Node* parent, Node* node){ 387 | if(!newValue){ 388 | if(!node->value){ 389 | return NOT_FOUND; 390 | } 391 | } 392 | 393 | if(!newValue && (!node->left || !node->right)){ 394 | bool prev; 395 | Node* damaged; 396 | 397 | { 398 | publish(parent); 399 | scoped_lock parentLock(parent->lock); 400 | 401 | if(isUnlinked(parent->version) || node->parent != parent){ 402 | releaseAll(); 403 | return RETRY; 404 | } 405 | 406 | { 407 | publish(node); 408 | scoped_lock lock(node->lock); 409 | 410 | prev = node->value; 411 | 412 | if(!shouldUpdate(func, prev, expected)){ 413 | releaseAll(); 414 | return noUpdateResult(func, prev); 415 | } 416 | 417 | if(!prev){ 418 | releaseAll(); 419 | return updateResult(func, prev); 420 | } 421 | 422 | if(!attemptUnlink_nl(parent, node)){ 423 | releaseAll(); 424 | return RETRY; 425 | } 426 | } 427 | 428 | releaseAll(); 429 | 430 | damaged = fixHeight_nl(parent); 431 | } 432 | 433 | fixHeightAndRebalance(damaged); 434 | 435 | return updateResult(func, prev); 436 | } else { 437 | publish(node); 438 | scoped_lock lock(node->lock); 439 | 440 | if(isUnlinked(node->version)){ 441 | releaseAll(); 442 | return RETRY; 443 | } 444 | 445 | bool prev = node->value; 446 | if(!shouldUpdate(func, prev, expected)){ 447 | releaseAll(); 448 | return noUpdateResult(func, prev); 449 | } 450 | 451 | if(!newValue && (!node->left || !node->right)){ 452 | releaseAll(); 453 | return RETRY; 454 | } 455 | 456 | node->value = newValue; 457 | 458 | releaseAll(); 459 | return updateResult(func, prev); 460 | } 461 | } 462 | 463 | template 464 | void AVLTree::waitUntilNotChanging(Node* node){ 465 | long version = node->version; 466 | 467 | if(isShrinking(version)){ 468 | for(int i = 0; i < SpinCount; ++i){ 469 | if(version != node->version){ 470 | return; 471 | } 472 | } 473 | 474 | node->lock.lock(); 475 | node->lock.unlock(); 476 | } 477 | } 478 | 479 | template 480 | bool AVLTree::attemptUnlink_nl(Node* parent, Node* node){ 481 | Node* parentL = parent->left; 482 | Node* parentR = parent->right; 483 | 484 | if(parentL != node && parentR != node){ 485 | return false; 486 | } 487 | 488 | Node* left = node->left; 489 | Node* right = node->right; 490 | 491 | if(left && right){ 492 | return false; 493 | } 494 | 495 | Node* splice = left ? left : right; 496 | 497 | if(parentL == node){ 498 | parent->left = splice; 499 | } else { 500 | parent->right = splice; 501 | } 502 | 503 | if(splice){ 504 | splice->parent = parent; 505 | } 506 | 507 | node->version = UnlinkedOVL; 508 | node->value = false; 509 | 510 | hazard.releaseNode(node); 511 | 512 | return true; 513 | } 514 | 515 | int height(Node* node){ 516 | return !node ? 0 : node->height; 517 | } 518 | 519 | int nodeCondition(Node* node){ 520 | Node* nL = node->left; 521 | Node* nR = node->right; 522 | 523 | // unlink is required 524 | if((!nL || !nR) && !node->value){ 525 | return UnlinkRequired; 526 | } 527 | 528 | int hN = node->height; 529 | int hL0 = height(nL); 530 | int hR0 = height(nR); 531 | int hNRepl = 1 + std::max(hL0, hR0); 532 | int bal = hL0 - hR0; 533 | 534 | // rebalance is required ? 535 | if(bal < -1 || bal > 1){ 536 | return RebalanceRequired; 537 | } 538 | 539 | return hN != hNRepl ? hNRepl : NothingRequired; 540 | } 541 | 542 | template 543 | void AVLTree::fixHeightAndRebalance(Node* node){ 544 | while(node && node->parent){ 545 | int condition = nodeCondition(node); 546 | if(condition == NothingRequired || isUnlinked(node->version)){ 547 | return; 548 | } 549 | 550 | if(condition != UnlinkRequired && condition != RebalanceRequired){ 551 | publish(node); 552 | scoped_lock lock(node->lock); 553 | 554 | node = fixHeight_nl(node); 555 | 556 | releaseAll(); 557 | } else { 558 | Node* nParent = node->parent; 559 | publish(nParent); 560 | scoped_lock lock(nParent->lock); 561 | 562 | if(!isUnlinked(nParent->version) && node->parent == nParent){ 563 | publish(node); 564 | scoped_lock nodeLock(node->lock); 565 | 566 | node = rebalance_nl(nParent, node); 567 | } 568 | 569 | releaseAll(); 570 | } 571 | } 572 | } 573 | 574 | Node* fixHeight_nl(Node* node){ 575 | int c = nodeCondition(node); 576 | 577 | switch(c){ 578 | case RebalanceRequired: 579 | case UnlinkRequired: 580 | return node; 581 | case NothingRequired: 582 | return nullptr; 583 | default: 584 | node->height = c; 585 | return node->parent; 586 | } 587 | } 588 | 589 | template 590 | Node* AVLTree::rebalance_nl(Node* nParent, Node* n){ 591 | Node* nL = n->left; 592 | Node* nR = n->right; 593 | 594 | if((!nL || !nR) && !n->value){ 595 | if(attemptUnlink_nl(nParent, n)){ 596 | return fixHeight_nl(nParent); 597 | } else { 598 | return n; 599 | } 600 | } 601 | 602 | int hN = n->height; 603 | int hL0 = height(nL); 604 | int hR0 = height(nR); 605 | int hNRepl = 1 + std::max(hL0, hR0); 606 | int bal = hL0 - hR0; 607 | 608 | if(bal > 1){ 609 | return rebalanceToRight_nl(nParent, n, nL, hR0); 610 | } else if(bal < -1){ 611 | return rebalanceToLeft_nl(nParent, n, nR, hL0); 612 | } else if(hNRepl != hN) { 613 | n->height = hNRepl; 614 | 615 | return fixHeight_nl(nParent); 616 | } else { 617 | return nullptr; 618 | } 619 | } 620 | 621 | template 622 | Node* AVLTree::rebalanceToRight_nl(Node* nParent, Node* n, Node* nL, int hR0){ 623 | publish(nL); 624 | scoped_lock lock(nL->lock); 625 | 626 | int hL = nL->height; 627 | if(hL - hR0 <= 1){ 628 | return n; 629 | } else { 630 | publish(nL->right); 631 | Node* nLR = nL->right; 632 | 633 | int hLL0 = height(nL->left); 634 | int hLR0 = height(nLR); 635 | 636 | if(hLL0 > hLR0){ 637 | return rotateRight_nl(nParent, n, nL, hR0, hLL0, nLR, hLR0); 638 | } else { 639 | { 640 | if(reinterpret_cast(&nLR->lock) == 0x30){ 641 | return n; 642 | } 643 | scoped_lock subLock(nLR->lock); 644 | 645 | int hLR = nLR->height; 646 | if(hLL0 >= hLR){ 647 | return rotateRight_nl(nParent, n, nL, hR0, hLL0, nLR, hLR); 648 | } else { 649 | int hLRL = height(nLR->left); 650 | int b = hLL0 - hLRL; 651 | if(b >= -1 && b <= 1){ 652 | return rotateRightOverLeft_nl(nParent, n, nL, hR0, hLL0, nLR, hLRL); 653 | } 654 | } 655 | } 656 | 657 | return rebalanceToLeft_nl(n, nL, nLR, hLL0); 658 | } 659 | } 660 | } 661 | 662 | template 663 | Node* AVLTree::rebalanceToLeft_nl(Node* nParent, Node* n, Node* nR, int hL0){ 664 | publish(nR); 665 | scoped_lock lock(nR->lock); 666 | 667 | int hR = nR->height; 668 | if(hL0 - hR >= -1){ 669 | return n; 670 | } else { 671 | Node* nRL = nR->left; 672 | int hRL0 = height(nRL); 673 | int hRR0 = height(nR->right); 674 | 675 | if(hRR0 >= hRL0){ 676 | return rotateLeft_nl(nParent, n, hL0, nR, nRL, hRL0, hRR0); 677 | } else { 678 | { 679 | publish(nRL); 680 | scoped_lock subLock(nRL->lock); 681 | 682 | int hRL = nRL->height; 683 | if(hRR0 >= hRL){ 684 | return rotateLeft_nl(nParent, n, hL0, nR, nRL, hRL, hRR0); 685 | } else { 686 | int hRLR = height(nRL->right); 687 | int b = hRR0 - hRLR; 688 | if(b >= -1 && b <= 1){ 689 | return rotateLeftOverRight_nl(nParent, n, hL0, nR, nRL, hRR0, hRLR); 690 | } 691 | } 692 | } 693 | 694 | return rebalanceToRight_nl(n, nR, nRL, hRR0); 695 | } 696 | } 697 | } 698 | 699 | template 700 | Node* AVLTree::rotateRight_nl(Node* nParent, Node* n, Node* nL, int hR, int hLL, Node* nLR, int hLR){ 701 | long nodeOVL = n->version; 702 | Node* nPL = nParent->left; 703 | n->version = beginChange(nodeOVL); 704 | 705 | n->left = nLR; 706 | if(nLR){ 707 | nLR->parent = n; 708 | } 709 | 710 | nL->right = n; 711 | n->parent = nL; 712 | 713 | if(nPL == n){ 714 | nParent->left = nL; 715 | } else { 716 | nParent->right = nL; 717 | } 718 | nL->parent = nParent; 719 | 720 | int hNRepl = 1 + std::max(hLR, hR); 721 | n->height = hNRepl; 722 | nL->height = 1 + std::max(hLL, hNRepl); 723 | 724 | n->version = endChange(nodeOVL); 725 | 726 | int balN = hLR - hR; 727 | if(balN < -1 || balN > 1){ 728 | return n; 729 | } 730 | 731 | int balL = hLL - hNRepl; 732 | if(balL < -1 || balL > 1){ 733 | return nL; 734 | } 735 | 736 | return fixHeight_nl(nParent); 737 | } 738 | 739 | template 740 | Node* AVLTree::rotateLeft_nl(Node* nParent, Node* n, int hL, Node* nR, Node* nRL, int hRL, int hRR){ 741 | long nodeOVL = n->version; 742 | Node* nPL = nParent->left; 743 | n->version = beginChange(nodeOVL); 744 | 745 | n->right = nRL; 746 | if(nRL){ 747 | nRL->parent = n; 748 | } 749 | 750 | nR->left = n; 751 | n->parent = nR; 752 | 753 | if(nPL == n){ 754 | nParent->left = nR; 755 | } else { 756 | nParent->right = nR; 757 | } 758 | nR->parent = nParent; 759 | 760 | int hNRepl = 1 + std::max(hL, hRL); 761 | n->height = hNRepl; 762 | nR->height = 1 + std::max(hNRepl, hRR); 763 | 764 | n->version = endChange(nodeOVL); 765 | 766 | int balN = hRL - hL; 767 | if(balN < -1 || balN > 1){ 768 | return n; 769 | } 770 | 771 | int balR = hRR - hNRepl; 772 | if(balR < -1 || balR > 1){ 773 | return nR; 774 | } 775 | 776 | return fixHeight_nl(nParent); 777 | } 778 | 779 | template 780 | Node* AVLTree::rotateRightOverLeft_nl(Node* nParent, Node* n, Node* nL, int hR, int hLL, Node* nLR, int hLRL){ 781 | long nodeOVL = n->version; 782 | long leftOVL = nL->version; 783 | 784 | Node* nPL = nParent->left; 785 | Node* nLRL = nLR->left; 786 | Node* nLRR = nLR->right; 787 | int hLRR = height(nLRR); 788 | 789 | n->version = beginChange(nodeOVL); 790 | nL->version = beginChange(leftOVL); 791 | 792 | n->left = nLRR; 793 | if(nLRR){ 794 | nLRR->parent = n; 795 | } 796 | 797 | nL->right = nLRL; 798 | if(nLRL){ 799 | nLRL->parent = nL; 800 | } 801 | 802 | nLR->left = nL; 803 | nL->parent = nLR; 804 | nLR->right = n; 805 | n->parent = nLR; 806 | 807 | if(nPL == n){ 808 | nParent->left = nLR; 809 | } else { 810 | nParent->right = nLR; 811 | } 812 | nLR->parent = nParent; 813 | 814 | int hNRepl = 1 + std::max(hLRR, hR); 815 | n->height = hNRepl; 816 | 817 | int hLRepl = 1 + std::max(hLL, hLRL); 818 | nL->height = hLRepl; 819 | 820 | nLR->height = 1 + std::max(hLRepl, hNRepl); 821 | 822 | n->version = endChange(nodeOVL); 823 | nL->version = endChange(leftOVL); 824 | 825 | int balN = hLRR - hR; 826 | if(balN < -1 || balN > 1){ 827 | return n; 828 | } 829 | 830 | int balLR = hLRepl - hNRepl; 831 | if(balLR < -1 || balLR > 1){ 832 | return nLR; 833 | } 834 | 835 | return fixHeight_nl(nParent); 836 | } 837 | 838 | template 839 | Node* AVLTree::rotateLeftOverRight_nl(Node* nParent, Node* n, int hL, Node* nR, Node* nRL, int hRR, int hRLR){ 840 | long nodeOVL = n->version; 841 | long rightOVL = nR->version; 842 | 843 | n->version = beginChange(nodeOVL); 844 | nR->version = beginChange(rightOVL); 845 | 846 | Node* nPL = nParent->left; 847 | Node* nRLL = nRL->left; 848 | Node* nRLR = nRL->right; 849 | int hRLL = height(nRLL); 850 | 851 | n->right = nRLL; 852 | if(nRLL){ 853 | nRLL->parent = n; 854 | } 855 | 856 | nR->left = nRLR; 857 | if(nRLR){ 858 | nRLR->parent = nR; 859 | } 860 | 861 | nRL->right = nR; 862 | nR->parent = nRL; 863 | nRL->left = n; 864 | n->parent = nRL; 865 | 866 | if(nPL == n){ 867 | nParent->left = nRL; 868 | } else { 869 | nParent->right = nRL; 870 | } 871 | nRL->parent = nParent; 872 | 873 | int hNRepl = 1 + std::max(hL, hRLL); 874 | n->height = hNRepl; 875 | int hRRepl = 1 + std::max(hRLR, hRR); 876 | nR->height = hRRepl; 877 | nRL->height = 1 + std::max(hNRepl, hRRepl); 878 | 879 | n->version = endChange(nodeOVL); 880 | nR->version = endChange(rightOVL); 881 | 882 | int balN = hRLL - hL; 883 | if(balN < -1 || balN > 1){ 884 | return n; 885 | } 886 | 887 | int balRL = hRRepl - hNRepl; 888 | if(balRL < -1 || balRL > 1){ 889 | return nRL; 890 | } 891 | 892 | return fixHeight_nl(nParent); 893 | } 894 | 895 | } //end of avltree 896 | 897 | #endif 898 | -------------------------------------------------------------------------------- /src/bench.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bench.hpp" 11 | #include "file_distribution.hpp" 12 | #include "HazardManager.hpp" //To manipulate thread_num 13 | #include "Results.hpp" //To generate the graphs data 14 | 15 | //Include all the trees implementations 16 | #include "skiplist/SkipList.hpp" 17 | #include "nbbst/NBBST.hpp" 18 | #include "avltree/AVLTree.hpp" 19 | #include "lfmst/MultiwaySearchTree.hpp" 20 | #include "cbtree/CBTree.hpp" 21 | 22 | //Benchmark constants 23 | #define OPERATIONS 1000000 24 | #define REPEAT 2 25 | #define SEARCH_BENCH_OPERATIONS 100000 26 | 27 | //Chrono typedefs 28 | typedef std::chrono::high_resolution_clock Clock; 29 | typedef std::chrono::milliseconds milliseconds; 30 | typedef std::chrono::microseconds microseconds; 31 | 32 | template 33 | void random_bench(const std::string& name, unsigned int range, unsigned int add, unsigned int remove, Results& results){ 34 | Tree tree; 35 | 36 | Clock::time_point t0 = Clock::now(); 37 | 38 | std::vector elements[Threads]; 39 | 40 | std::vector pool; 41 | for(unsigned int tid = 0; tid < Threads; ++tid){ 42 | pool.push_back(std::thread([&tree, &elements, range, add, remove, tid](){ 43 | thread_num = tid; 44 | 45 | std::mt19937_64 engine(time(0) + tid); 46 | 47 | std::uniform_int_distribution valueDistribution(0, range); 48 | auto valueGenerator = std::bind(valueDistribution, engine); 49 | 50 | std::uniform_int_distribution operationDistribution(0, 99); 51 | auto operationGenerator = std::bind(operationDistribution, engine); 52 | 53 | for(int i = 0; i < OPERATIONS; ++i){ 54 | unsigned int value = valueGenerator(); 55 | unsigned int op = operationGenerator(); 56 | 57 | if(op < add){ 58 | if(tree.add(value)){ 59 | elements[thread_num].push_back(value); 60 | } 61 | } else if(op < (add + remove)){ 62 | tree.remove(value); 63 | } else { 64 | tree.contains(i); 65 | } 66 | } 67 | })); 68 | } 69 | 70 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 71 | 72 | Clock::time_point t1 = Clock::now(); 73 | 74 | milliseconds ms = std::chrono::duration_cast(t1 - t0); 75 | unsigned long throughput = (Threads * OPERATIONS) / ms.count(); 76 | 77 | std::cout << name << " througput with " << Threads << " threads = " << throughput << " operations / ms" << std::endl; 78 | 79 | results.add_result(name, throughput); 80 | 81 | pool.clear(); 82 | for(unsigned int tid = 0; tid < Threads; ++tid){ 83 | pool.push_back(std::thread([&tree, &elements, tid](){ 84 | thread_num = tid; 85 | 86 | for(auto i : elements[thread_num]){ 87 | tree.remove(i); 88 | } 89 | })); 90 | } 91 | 92 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 93 | } 94 | 95 | #define BENCH(type, name, range, add, remove)\ 96 | random_bench, 1>(name, range, add, remove, results);\ 97 | random_bench, 2>(name, range, add, remove, results);\ 98 | random_bench, 3>(name, range, add, remove, results);\ 99 | random_bench, 4>(name, range, add, remove, results);\ 100 | random_bench, 8>(name, range, add, remove, results);\ 101 | random_bench, 16>(name, range, add, remove, results);\ 102 | random_bench, 32>(name, range, add, remove, results); 103 | 104 | void random_bench(unsigned int range, unsigned int add, unsigned int remove){ 105 | std::cout << "Bench with " << OPERATIONS << " operations/thread, range = " << range << ", " << add << "% add, " << remove << "% remove, " << (100 - add - remove) << "% contains" << std::endl; 106 | 107 | std::stringstream bench_name; 108 | bench_name << "random-" << range << "-" << add << "-" << remove; 109 | 110 | Results results; 111 | results.start(bench_name.str()); 112 | results.set_max(7); 113 | 114 | for(int i = 0; i < REPEAT; ++i){ 115 | BENCH(skiplist::SkipList, "skiplist", range, add, remove); 116 | BENCH(nbbst::NBBST, "nbbst", range, add, remove); 117 | BENCH(avltree::AVLTree, "avltree", range, add, remove) 118 | BENCH(lfmst::MultiwaySearchTree, "lfmst", range, add, remove); 119 | BENCH(cbtree::CBTree, "cbtree", range, add, remove); 120 | } 121 | 122 | results.finish(); 123 | 124 | std::cout << "bench is over" << std::endl; 125 | } 126 | 127 | void random_bench(unsigned int range){ 128 | random_bench(range, 50, 50); //50% put, 50% remove, 0% contains 129 | random_bench(range, 20, 10); //20% put, 10% remove, 70% contains 130 | random_bench(range, 9, 1); //9% put, 1% remove, 90% contains 131 | } 132 | 133 | void random_bench(){ 134 | random_bench(200); //Key in {0, 200} 135 | random_bench(2000); //Key in {0, 2000} 136 | random_bench(20000); //Key in {0, 20000} 137 | 138 | //Note: The max number itself cannot be managed by some impl 139 | random_bench(std::numeric_limits::max() - 1); //Key in {0, 2^32} 140 | } 141 | 142 | template 143 | void skewed_bench(const std::string& name, unsigned int range, unsigned int add, unsigned int remove, file_distribution<>& distribution, Results& results){ 144 | Tree tree; 145 | 146 | std::vector elements[Threads]; 147 | 148 | Clock::time_point t0 = Clock::now(); 149 | 150 | std::vector pool; 151 | for(unsigned int tid = 0; tid < Threads; ++tid){ 152 | pool.push_back(std::thread([&tree, &elements, &distribution, range, add, remove, tid](){ 153 | thread_num = tid; 154 | 155 | std::mt19937_64 engine(time(0) + tid); 156 | 157 | std::uniform_int_distribution operationDistribution(0, 99); 158 | auto operationGenerator = std::bind(operationDistribution, engine); 159 | 160 | for(int i = 0; i < OPERATIONS; ++i){ 161 | auto value = distribution(engine); 162 | if(tree.add(value)){ 163 | elements[thread_num].push_back(value); 164 | } 165 | } 166 | 167 | for(int i = 0; i < OPERATIONS; ++i){ 168 | unsigned int value = distribution(engine); 169 | unsigned int op = operationGenerator(); 170 | 171 | if(op < add){ 172 | if(tree.add(value)){ 173 | elements[thread_num].push_back(value); 174 | } 175 | } else if(op < (add + remove)){ 176 | tree.remove(value); 177 | } else { 178 | tree.contains(i); 179 | } 180 | } 181 | })); 182 | } 183 | 184 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 185 | 186 | Clock::time_point t1 = Clock::now(); 187 | 188 | milliseconds ms = std::chrono::duration_cast(t1 - t0); 189 | unsigned long throughput = (Threads * 2 * OPERATIONS) / ms.count(); 190 | 191 | std::cout << name << " througput with " << Threads << " threads = " << throughput << " operations / ms" << std::endl; 192 | results.add_result(name, throughput); 193 | 194 | pool.clear(); 195 | for(unsigned int tid = 0; tid < Threads; ++tid){ 196 | pool.push_back(std::thread([&tree, &elements, tid](){ 197 | thread_num = tid; 198 | 199 | for(auto i : elements[thread_num]){ 200 | tree.remove(i); 201 | } 202 | })); 203 | } 204 | 205 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 206 | } 207 | 208 | void skewed_bench(unsigned int range, unsigned int add, unsigned int remove, file_distribution<>& distribution, Results& results){ 209 | std::cout << "Skewed Bench with " << OPERATIONS << " operations/thread, range = " << range << ", " << add << "% add, " << remove << "% remove, " << (100 - add - remove) << "% contains" << std::endl; 210 | 211 | for(int i = 0; i < REPEAT; ++i){ 212 | skewed_bench, 8>("skiplist", range, add, remove, distribution, results); 213 | skewed_bench, 8>("nbbst", range, add, remove, distribution, results); 214 | skewed_bench, 8>("avltree", range, add, remove, distribution, results); 215 | skewed_bench, 8>("lfmst", range, add, remove, distribution, results); 216 | skewed_bench, 8>("cbtree", range, add, remove, distribution, results); 217 | } 218 | } 219 | 220 | void skewed_bench(unsigned int range){ 221 | std::stringstream range_name; 222 | range_name << range; 223 | 224 | Results results; 225 | results.start("skewed-" + range_name.str()); 226 | results.set_max(11); 227 | 228 | file_distribution<> distribution("zipf/zipf-00-" + range_name.str(), 1000000); 229 | skewed_bench(range, 10, 0, distribution, results); 230 | 231 | distribution = file_distribution<>("zipf/zipf-02-" + range_name.str(), 1000000); 232 | skewed_bench(range, 10, 0, distribution, results); 233 | 234 | distribution = file_distribution<>("zipf/zipf-04-" + range_name.str(), 1000000); 235 | skewed_bench(range, 10, 0, distribution, results); 236 | 237 | distribution = file_distribution<>("zipf/zipf-06-" + range_name.str(), 1000000); 238 | skewed_bench(range, 10, 0, distribution, results); 239 | 240 | distribution = file_distribution<>("zipf/zipf-08-" + range_name.str(), 1000000); 241 | skewed_bench(range, 10, 0, distribution, results); 242 | 243 | distribution = file_distribution<>("zipf/zipf-10-" + range_name.str(), 1000000); 244 | skewed_bench(range, 10, 0, distribution, results); 245 | 246 | distribution = file_distribution<>("zipf/zipf-12-" + range_name.str(), 1000000); 247 | skewed_bench(range, 10, 0, distribution, results); 248 | 249 | distribution = file_distribution<>("zipf/zipf-14-" + range_name.str(), 1000000); 250 | skewed_bench(range, 10, 0, distribution, results); 251 | 252 | distribution = file_distribution<>("zipf/zipf-16-" + range_name.str(), 1000000); 253 | skewed_bench(range, 10, 0, distribution, results); 254 | 255 | distribution = file_distribution<>("zipf/zipf-18-" + range_name.str(), 1000000); 256 | skewed_bench(range, 10, 0, distribution, results); 257 | 258 | distribution = file_distribution<>("zipf/zipf-20-" + range_name.str(), 1000000); 259 | skewed_bench(range, 10, 0, distribution, results); 260 | 261 | results.finish(); 262 | 263 | std::cout << "bench is over" << std::endl; 264 | } 265 | 266 | void skewed_bench(){ 267 | skewed_bench(2000); 268 | skewed_bench(20000); 269 | skewed_bench(200000); 270 | } 271 | 272 | unsigned long get_duration(Clock::time_point t0, Clock::time_point t1){ 273 | milliseconds ms = std::chrono::duration_cast(t1 - t0); 274 | 275 | return ms.count(); 276 | } 277 | 278 | template 279 | void seq_construction_bench(const std::string& name, unsigned int size, Results& results){ 280 | Tree tree; 281 | 282 | Clock::time_point t0 = Clock::now(); 283 | 284 | unsigned int part = size / Threads; 285 | 286 | std::vector pool; 287 | for(unsigned int tid = 0; tid < Threads; ++tid){ 288 | pool.push_back(std::thread([&tree, part, size, tid](){ 289 | thread_num = tid; 290 | 291 | for(unsigned int i = tid * part; i < (tid + 1) * part; ++i){ 292 | tree.add(i); 293 | } 294 | })); 295 | } 296 | 297 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 298 | 299 | Clock::time_point t1 = Clock::now(); 300 | 301 | std::cout << "Construction of " << name << " with " << size << " elements took " << get_duration(t0, t1) << " ms with " << Threads << " threads" << std::endl; 302 | results.add_result(name, get_duration(t0, t1)); 303 | 304 | //Empty the tree 305 | for(unsigned int i = 0; i < size; ++i){ 306 | tree.remove(i); 307 | } 308 | } 309 | 310 | #define SEQ_CONSTRUCTION(type, name, size)\ 311 | seq_construction_bench, 1>(name, size, results);\ 312 | seq_construction_bench, 2>(name, size, results);\ 313 | seq_construction_bench, 3>(name, size, results);\ 314 | seq_construction_bench, 4>(name, size, results);\ 315 | seq_construction_bench, 8>(name, size, results); 316 | 317 | void seq_construction_bench(){ 318 | std::cout << "Bench the sequential construction time of each data structure" << std::endl; 319 | 320 | std::vector small_sizes = {1000, 5000, 10000}; 321 | 322 | for(auto size : small_sizes){ 323 | std::stringstream name; 324 | name << "sequential-build-" << size; 325 | 326 | Results results; 327 | results.start(name.str()); 328 | results.set_max(5); 329 | 330 | for(int i = 0; i < REPEAT; ++i){ 331 | SEQ_CONSTRUCTION(skiplist::SkipList, "skiplist", size); 332 | SEQ_CONSTRUCTION(nbbst::NBBST, "nbbst", size); 333 | SEQ_CONSTRUCTION(avltree::AVLTree, "avltree", size); 334 | SEQ_CONSTRUCTION(lfmst::MultiwaySearchTree, "lfmst", size); 335 | SEQ_CONSTRUCTION(cbtree::CBTree, "cbtree", size); 336 | } 337 | 338 | results.finish(); 339 | 340 | std::cout << "bench is over" << std::endl; 341 | } 342 | 343 | std::vector sizes = {50000, 100000, 500000, 1000000, 5000000, 10000000}; 344 | 345 | for(auto size : sizes){ 346 | std::stringstream name; 347 | name << "sequential-build-" << size; 348 | 349 | Results results; 350 | results.start(name.str()); 351 | results.set_max(5); 352 | 353 | for(int i = 0; i < REPEAT; ++i){ 354 | SEQ_CONSTRUCTION(skiplist::SkipList, "skiplist", size); 355 | //Too slow SEQ_CONSTRUCTION(nbbst::NBBST, "nbbst", size); 356 | SEQ_CONSTRUCTION(avltree::AVLTree, "avltree", size); 357 | SEQ_CONSTRUCTION(lfmst::MultiwaySearchTree, "lfmst", size); 358 | SEQ_CONSTRUCTION(cbtree::CBTree, "cbtree", size); 359 | } 360 | 361 | results.finish(); 362 | 363 | std::cout << "bench is over" << std::endl; 364 | } 365 | } 366 | 367 | template 368 | void random_construction_bench(const std::string& name, unsigned int size, Results& results){ 369 | Tree tree; 370 | 371 | std::vector elements; 372 | for(unsigned int i = 0; i < size; ++i){ 373 | elements.push_back(i); 374 | } 375 | 376 | random_shuffle(elements.begin(), elements.end()); 377 | 378 | Clock::time_point t0 = Clock::now(); 379 | 380 | unsigned int part = size / Threads; 381 | 382 | std::vector pool; 383 | for(unsigned int tid = 0; tid < Threads; ++tid){ 384 | pool.push_back(std::thread([&tree, &elements, part, size, tid](){ 385 | thread_num = tid; 386 | 387 | for(unsigned int i = tid * part; i < (tid + 1) * part; ++i){ 388 | tree.add(elements[i]); 389 | } 390 | })); 391 | } 392 | 393 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 394 | 395 | Clock::time_point t1 = Clock::now(); 396 | 397 | std::cout << "Construction of " << name << " with " << size << " elements took " << get_duration(t0, t1) << " ms with " << Threads << " threads" << std::endl; 398 | results.add_result(name, get_duration(t0, t1)); 399 | 400 | //Empty the tree 401 | for(unsigned int i = 0; i < size; ++i){ 402 | tree.remove(i); 403 | } 404 | } 405 | 406 | #define RANDOM_CONSTRUCTION(type, name, size)\ 407 | random_construction_bench, 1>(name, size, results);\ 408 | random_construction_bench, 2>(name, size, results);\ 409 | random_construction_bench, 3>(name, size, results);\ 410 | random_construction_bench, 4>(name, size, results);\ 411 | random_construction_bench, 8>(name, size, results); 412 | 413 | void random_construction_bench(){ 414 | std::cout << "Bench the random construction time of each data structure" << std::endl; 415 | 416 | std::vector sizes = {50000, 100000, 500000, 1000000, 5000000, 10000000}; 417 | 418 | for(auto size : sizes){ 419 | std::stringstream name; 420 | name << "random-build-" << size; 421 | 422 | Results results; 423 | results.start(name.str()); 424 | results.set_max(5); 425 | 426 | for(int i = 0; i < REPEAT; ++i){ 427 | RANDOM_CONSTRUCTION(skiplist::SkipList, "skiplist", size); 428 | RANDOM_CONSTRUCTION(nbbst::NBBST, "nbbst", size); 429 | RANDOM_CONSTRUCTION(avltree::AVLTree, "avltree", size); 430 | RANDOM_CONSTRUCTION(lfmst::MultiwaySearchTree, "lfmst", size); 431 | RANDOM_CONSTRUCTION(cbtree::CBTree, "cbtree", size); 432 | } 433 | 434 | results.finish(); 435 | 436 | std::cout << "bench is over" << std::endl; 437 | } 438 | } 439 | 440 | template 441 | void seq_removal_bench(const std::string& name, unsigned int size, Results& results){ 442 | Tree tree; 443 | 444 | for(unsigned int i = 0; i < size; ++i){ 445 | tree.add(i); 446 | } 447 | 448 | unsigned int part = size / Threads; 449 | 450 | Clock::time_point t0 = Clock::now(); 451 | 452 | std::vector pool; 453 | for(unsigned int tid = 0; tid < Threads; ++tid){ 454 | pool.push_back(std::thread([&tree, part, size, tid](){ 455 | thread_num = tid; 456 | 457 | for(unsigned int i = tid * part; i < (tid + 1) * part; ++i){ 458 | tree.remove(i); 459 | } 460 | })); 461 | } 462 | 463 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 464 | 465 | Clock::time_point t1 = Clock::now(); 466 | 467 | std::cout << "Removal of " << name << " with " << size << " elements took " << get_duration(t0, t1) << " ms with " << Threads << " threads" << std::endl; 468 | results.add_result(name, get_duration(t0, t1)); 469 | } 470 | 471 | #define SEQUENTIAL_REMOVAL(type, name, size)\ 472 | seq_removal_bench, 1>(name, size, results);\ 473 | seq_removal_bench, 2>(name, size, results);\ 474 | seq_removal_bench, 3>(name, size, results);\ 475 | seq_removal_bench, 4>(name, size, results);\ 476 | seq_removal_bench, 8>(name, size, results); 477 | 478 | void seq_removal_bench(){ 479 | std::cout << "Bench the sequential removal time of each data structure" << std::endl; 480 | 481 | std::vector small_sizes = {1000, 5000, 10000}; 482 | 483 | for(auto size : small_sizes){ 484 | std::stringstream name; 485 | name << "sequential-removal-" << size; 486 | 487 | Results results; 488 | results.start(name.str()); 489 | results.set_max(5); 490 | 491 | for(int i = 0; i < REPEAT; ++i){ 492 | SEQUENTIAL_REMOVAL(skiplist::SkipList, "skiplist", size); 493 | SEQUENTIAL_REMOVAL(nbbst::NBBST, "nbbst", size); 494 | SEQUENTIAL_REMOVAL(avltree::AVLTree, "avltree", size); 495 | SEQUENTIAL_REMOVAL(lfmst::MultiwaySearchTree, "lfmst", size); 496 | SEQUENTIAL_REMOVAL(cbtree::CBTree, "cbtree", size); 497 | } 498 | 499 | results.finish(); 500 | 501 | std::cout << "bench is over" << std::endl; 502 | } 503 | 504 | std::vector sizes = {50000, 100000, 500000, 1000000, 5000000, 10000000}; 505 | 506 | for(auto size : sizes){ 507 | std::stringstream name; 508 | name << "sequential-removal-" << size; 509 | 510 | Results results; 511 | results.start(name.str()); 512 | results.set_max(5); 513 | 514 | for(int i = 0; i < REPEAT; ++i){ 515 | SEQUENTIAL_REMOVAL(skiplist::SkipList, "skiplist", size); 516 | //Too slow SEQUENTIAL_REMOVAL(nbbst::NBBST, "NBBST", size); 517 | SEQUENTIAL_REMOVAL(avltree::AVLTree, "avltree", size); 518 | SEQUENTIAL_REMOVAL(lfmst::MultiwaySearchTree, "lfmst", size); 519 | SEQUENTIAL_REMOVAL(cbtree::CBTree, "cbtree", size); 520 | } 521 | 522 | results.finish(); 523 | 524 | std::cout << "bench is over" << std::endl; 525 | } 526 | } 527 | 528 | template 529 | void random_removal_bench(const std::string& name, unsigned int size, Results& results){ 530 | Tree tree; 531 | 532 | std::vector elements; 533 | for(unsigned int i = 0; i < size; ++i){ 534 | elements.push_back(i); 535 | } 536 | 537 | random_shuffle(elements.begin(), elements.end()); 538 | 539 | for(unsigned int i = 0; i < size; ++i){ 540 | tree.add(elements[i]); 541 | } 542 | 543 | unsigned int part = size / Threads; 544 | 545 | Clock::time_point t0 = Clock::now(); 546 | 547 | std::vector pool; 548 | for(unsigned int tid = 0; tid < Threads; ++tid){ 549 | pool.push_back(std::thread([&tree, &elements, part, size, tid](){ 550 | thread_num = tid; 551 | 552 | for(unsigned int i = tid * part; i < (tid + 1) * part; ++i){ 553 | tree.remove(elements[i]); 554 | } 555 | })); 556 | } 557 | 558 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 559 | 560 | Clock::time_point t1 = Clock::now(); 561 | 562 | std::cout << "Removal of " << name << " with " << size << " elements took " << get_duration(t0, t1) << " ms with " << Threads << " threads" << std::endl; 563 | results.add_result(name, get_duration(t0, t1)); 564 | } 565 | 566 | #define RANDOM_REMOVAL(type, name, size)\ 567 | random_removal_bench, 1>(name, size, results);\ 568 | random_removal_bench, 2>(name, size, results);\ 569 | random_removal_bench, 3>(name, size, results);\ 570 | random_removal_bench, 4>(name, size, results);\ 571 | random_removal_bench, 8>(name, size, results); 572 | 573 | void random_removal_bench(){ 574 | std::cout << "Bench the random removal time of each data structure" << std::endl; 575 | 576 | std::vector sizes = {50000, 100000, 500000, 1000000, 5000000, 10000000}; 577 | 578 | for(auto size : sizes){ 579 | std::stringstream name; 580 | name << "random-removal-" << size; 581 | 582 | Results results; 583 | results.start(name.str()); 584 | results.set_max(5); 585 | 586 | for(int i = 0; i < REPEAT; ++i){ 587 | RANDOM_REMOVAL(skiplist::SkipList, "skiplist", size); 588 | RANDOM_REMOVAL(nbbst::NBBST, "nbbst", size); 589 | RANDOM_REMOVAL(avltree::AVLTree, "avltree", size); 590 | RANDOM_REMOVAL(lfmst::MultiwaySearchTree, "lfmst", size); 591 | RANDOM_REMOVAL(cbtree::CBTree, "cbtree", size); 592 | } 593 | 594 | results.finish(); 595 | 596 | std::cout << "bench is over" << std::endl; 597 | } 598 | } 599 | 600 | template 601 | void search_bench(const std::string& name, unsigned int size, Tree& tree, Results& results){ 602 | Clock::time_point t0 = Clock::now(); 603 | 604 | std::vector pool; 605 | for(unsigned int tid = 0; tid < Threads; ++tid){ 606 | pool.push_back(std::thread([&tree, size, tid](){ 607 | thread_num = tid; 608 | 609 | std::mt19937_64 engine(time(0) + tid); 610 | std::uniform_int_distribution distribution(0, size); 611 | 612 | for(int s = 0; s < SEARCH_BENCH_OPERATIONS; ++s){ 613 | tree.contains(distribution(engine)); 614 | } 615 | })); 616 | } 617 | 618 | for_each(pool.begin(), pool.end(), [](std::thread& t){t.join();}); 619 | 620 | Clock::time_point t1 = Clock::now(); 621 | 622 | milliseconds ms = std::chrono::duration_cast(t1 - t0); 623 | unsigned long throughput = (Threads * SEARCH_BENCH_OPERATIONS) / ms.count(); 624 | 625 | std::cout << name << "-" << size << " search througput with " << Threads << " threads = " << throughput << " operations / ms" << std::endl; 626 | results.add_result(name, throughput); 627 | } 628 | 629 | template 630 | void fill_random(Tree& tree, unsigned int size){ 631 | std::vector values; 632 | for(unsigned int i = 0; i < size; ++i){ 633 | values.push_back(i); 634 | } 635 | 636 | random_shuffle(values.begin(), values.end()); 637 | 638 | for(unsigned int i = 0; i < size; ++i){ 639 | tree.add(values[i]); 640 | } 641 | } 642 | 643 | template 644 | void fill_sequential(Tree& tree, unsigned int size){ 645 | for(unsigned int i = 0; i < size; ++i){ 646 | tree.add(i); 647 | } 648 | } 649 | 650 | template 651 | void search_random_bench(const std::string& name, unsigned int size, Results& results){ 652 | Tree tree; 653 | 654 | fill_random(tree, size); 655 | 656 | search_bench(name, size, tree, results); 657 | 658 | //Empty the tree 659 | for(unsigned int i = 0; i < size; ++i){ 660 | tree.remove(i); 661 | } 662 | } 663 | 664 | #define SEARCH_RANDOM(type, name, size)\ 665 | search_random_bench, 1>(name, size, results);\ 666 | search_random_bench, 2>(name, size, results);\ 667 | search_random_bench, 3>(name, size, results);\ 668 | search_random_bench, 4>(name, size, results);\ 669 | search_random_bench, 8>(name, size, results); 670 | 671 | void search_random_bench(){ 672 | std::cout << "Bench the search performances of each data structure with random insertion" << std::endl; 673 | 674 | std::vector sizes = {50000, 100000, 500000, 1000000, 5000000, 10000000}; 675 | 676 | for(auto size : sizes){ 677 | std::stringstream name; 678 | name << "random-search-" << size; 679 | 680 | Results results; 681 | results.start(name.str()); 682 | results.set_max(5); 683 | 684 | for(int i = 0; i < REPEAT; ++i){ 685 | SEARCH_RANDOM(skiplist::SkipList, "skiplist", size); 686 | SEARCH_RANDOM(nbbst::NBBST, "nbbst", size); 687 | SEARCH_RANDOM(avltree::AVLTree, "avltree", size); 688 | SEARCH_RANDOM(lfmst::MultiwaySearchTree, "lfmst", size); 689 | SEARCH_RANDOM(cbtree::CBTree, "cbtree", size); 690 | } 691 | 692 | results.finish(); 693 | 694 | std::cout << "bench is over" << std::endl; 695 | } 696 | } 697 | 698 | template 699 | void search_sequential_bench(const std::string& name, unsigned int size, Results& results){ 700 | Tree tree; 701 | 702 | fill_sequential(tree, size); 703 | 704 | search_bench(name, size, tree, results); 705 | 706 | //Empty the tree 707 | for(unsigned int i = 0; i < size; ++i){ 708 | tree.remove(i); 709 | } 710 | } 711 | 712 | #define SEARCH_SEQUENTIAL(type, name, size)\ 713 | search_sequential_bench, 1>(name, size, results);\ 714 | search_sequential_bench, 2>(name, size, results);\ 715 | search_sequential_bench, 3>(name, size, results);\ 716 | search_sequential_bench, 4>(name, size, results);\ 717 | search_sequential_bench, 8>(name, size, results); 718 | 719 | void search_sequential_bench(){ 720 | std::cout << "Bench the search performances of each data structure with sequential insertion" << std::endl; 721 | 722 | std::vector small_sizes = {1000, 5000, 10000}; 723 | 724 | for(auto size : small_sizes){ 725 | std::stringstream name; 726 | name << "sequential-search-" << size; 727 | 728 | Results results; 729 | results.start(name.str()); 730 | results.set_max(5); 731 | 732 | for(int i = 0; i < REPEAT; ++i){ 733 | SEARCH_SEQUENTIAL(skiplist::SkipList, "skiplist", size); 734 | SEARCH_SEQUENTIAL(nbbst::NBBST, "nbbst", size); 735 | SEARCH_SEQUENTIAL(avltree::AVLTree, "avltree", size); 736 | SEARCH_SEQUENTIAL(lfmst::MultiwaySearchTree, "lfmst", size); 737 | SEARCH_SEQUENTIAL(cbtree::CBTree, "cbtree", size); 738 | } 739 | 740 | results.finish(); 741 | 742 | std::cout << "bench is over" << std::endl; 743 | } 744 | 745 | std::vector sizes = {50000, 100000, 500000, 1000000, 5000000, 10000000}; 746 | 747 | for(auto size : sizes){ 748 | std::stringstream name; 749 | name << "sequential-search-" << size; 750 | 751 | Results results; 752 | results.start(name.str()); 753 | results.set_max(5); 754 | 755 | for(int i = 0; i < REPEAT; ++i){ 756 | SEARCH_SEQUENTIAL(skiplist::SkipList, "skiplist", size); 757 | //The nbbst is far too slow SEARCH_SEQUENTIAL(nbbst::NBBST, "nbbst", size); 758 | SEARCH_SEQUENTIAL(avltree::AVLTree, "avltree", size); 759 | SEARCH_SEQUENTIAL(lfmst::MultiwaySearchTree, "lfmst", size); 760 | SEARCH_SEQUENTIAL(cbtree::CBTree, "cbtree", size); 761 | } 762 | 763 | results.finish(); 764 | 765 | std::cout << "bench is over" << std::endl; 766 | } 767 | } 768 | 769 | void bench(){ 770 | std::cout << "Tests the performance of the different versions" << std::endl; 771 | 772 | //Launch the random benchmark 773 | random_bench(); 774 | skewed_bench(); 775 | 776 | //Launch the construction benchmark 777 | random_construction_bench(); 778 | seq_construction_bench(); 779 | 780 | //Launch the removal benchmark 781 | random_removal_bench(); 782 | seq_removal_bench(); 783 | 784 | //Launch the search benchmark 785 | search_random_bench(); 786 | search_sequential_bench(); 787 | } 788 | -------------------------------------------------------------------------------- /include/cbtree/CBTree.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CB_TREE 2 | #define CB_TREE 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Utils.hpp" 9 | #include "HazardManager.hpp" 10 | 11 | namespace cbtree { 12 | 13 | typedef std::lock_guard scoped_lock; 14 | 15 | static const int SpinCount = 100; 16 | 17 | static const char Left = 'L'; 18 | static const char Right = 'R'; 19 | 20 | /* Version stuff */ 21 | static const long OVLBitsBeforeOverflow = 8; 22 | static const long UnlinkedOVL = 1L; 23 | static const long OVLGrowLockMask = 2L; 24 | static const long OVLShrinkLockMask = 4L; 25 | static const long OVLGrowCountShift = 3L; 26 | static const long OVLGrowCountMask = ((1L << OVLBitsBeforeOverflow) - 1) << OVLGrowCountShift; 27 | static const long OVLShrinkCountShift = OVLGrowCountShift + OVLBitsBeforeOverflow; 28 | 29 | static inline bool isChanging( long ovl) { 30 | return (ovl & (OVLShrinkLockMask | OVLGrowLockMask)) != 0; 31 | } 32 | 33 | static inline bool isUnlinked( long ovl) { 34 | return ovl == UnlinkedOVL; 35 | } 36 | 37 | static inline bool isShrinkingOrUnlinked( long ovl) { 38 | return (ovl & (OVLShrinkLockMask | UnlinkedOVL)) != 0; 39 | } 40 | 41 | static inline bool isChangingOrUnlinked( long ovl) { 42 | return (ovl & (OVLShrinkLockMask | OVLGrowLockMask | UnlinkedOVL)) != 0; 43 | } 44 | 45 | static inline bool hasShrunkOrUnlinked( long orig, long current) { 46 | return ((orig ^ current) & ~(OVLGrowLockMask | OVLGrowCountMask)) != 0; 47 | } 48 | 49 | static inline long beginGrow( long ovl) { 50 | return ovl | OVLGrowLockMask; 51 | } 52 | 53 | static inline long endGrow( long ovl) { 54 | // Overflows will just go into the shrink lock count, which is fine. 55 | return ovl + (1L << OVLGrowCountShift); 56 | } 57 | 58 | static inline long beginShrink( long ovl) { 59 | return ovl | OVLShrinkLockMask; 60 | } 61 | 62 | static inline long endShrink( long ovl) { 63 | // increment overflows directly 64 | return ovl + (1L << OVLShrinkCountShift); 65 | } 66 | 67 | struct Node { 68 | int key; 69 | bool value; 70 | 71 | Node* parent; 72 | long changeOVL; 73 | 74 | Node* left; 75 | Node* right; 76 | 77 | int ncnt; 78 | int rcnt; 79 | int lcnt; 80 | 81 | std::mutex lock; 82 | 83 | Node* child(char dir){ 84 | return dir == Left ? left : right; 85 | } 86 | 87 | void setChild(char dir, Node* node) { 88 | if (dir == Left) { 89 | left = node; 90 | } else { 91 | right = node; 92 | } 93 | } 94 | 95 | void waitUntilChangeCompleted(long ovl) { 96 | if (!isChanging(ovl)) { 97 | return; 98 | } 99 | 100 | for (int tries = 0; tries < SpinCount; ++tries) { 101 | if (changeOVL != ovl) { 102 | return; 103 | } 104 | } 105 | 106 | lock.lock(); 107 | lock.unlock(); 108 | } 109 | }; 110 | 111 | enum Result { 112 | FOUND, 113 | NOT_FOUND, 114 | RETRY 115 | }; 116 | 117 | template 118 | class CBTree { 119 | public: 120 | CBTree(); 121 | ~CBTree(); 122 | 123 | bool add(T value); 124 | bool remove(T value); 125 | bool contains(T value); 126 | 127 | private: 128 | Node* rootHolder; 129 | 130 | std::atomic size; 131 | std::atomic logSize; 132 | int local_size[Threads]; 133 | 134 | int NEW_LOG_CALCULATION_THRESHOLD; 135 | 136 | void publish(Node* ref); 137 | void releaseAll(); 138 | 139 | HazardManager hazard; 140 | 141 | unsigned int Current[Threads]; 142 | void deep_release(Node* node); 143 | 144 | /* Allocate new nodes */ 145 | Node* newNode(int key, bool value, Node* parent, long changeOVL, Node* left, Node* right); 146 | 147 | /* Internal stuff */ 148 | Result getImpl(int key); 149 | Result update(int key); 150 | Result attemptRemove(int key, Node* parent, Node* node, long nodeOVL, int height); 151 | Result attemptGet(int key, Node* node, char dirToc, long nodeOVL, int height); 152 | bool attemptInsertIntoEmpty(int key); 153 | Result attemptUpdate(int key, Node* parent, Node* node, long nodeOVL, int height); 154 | Result attemptNodeUpdate(bool newValue, Node* parent, Node* node); 155 | bool attemptUnlink_nl(Node* parent, Node* node); 156 | 157 | /* Balancing */ 158 | void SemiSplay(Node* child); 159 | void RebalanceAtTarget(Node* parent, Node* node); 160 | void RebalanceNew(Node* parent, char dirToC); 161 | 162 | /* Rotating stuff */ 163 | void rotateRight(Node* nParent, Node* n, Node* nL, Node* nLR); 164 | void rotateLeft(Node* nParent, Node* n, Node* nR, Node* nRL); 165 | void rotateRightOverLeft(Node* nParent, Node* n, Node* nL, Node* nLR); 166 | void rotateLeftOverRight(Node* nParent, Node* n, Node* nR, Node* nRL); 167 | }; 168 | 169 | template 170 | CBTree::CBTree(){ 171 | rootHolder = newNode(std::numeric_limits::min(), false, nullptr, 0L, nullptr, nullptr); 172 | rootHolder->ncnt = std::numeric_limits::max(); 173 | 174 | size.store(0); 175 | logSize.store(-1); 176 | 177 | for(int i = 0; i < Threads; ++i){ 178 | local_size[i] = 0; 179 | Current[i] = 0; 180 | } 181 | 182 | NEW_LOG_CALCULATION_THRESHOLD = 15;//std::log(2 * Threads * Threads); 183 | } 184 | 185 | template 186 | void CBTree::deep_release(Node* node){ 187 | if(node->left){ 188 | deep_release(node->left); 189 | } 190 | 191 | if(node->right){ 192 | deep_release(node->right); 193 | } 194 | 195 | if(!isUnlinked(node->changeOVL)){ 196 | hazard.releaseNode(node); 197 | } 198 | } 199 | 200 | template 201 | CBTree::~CBTree(){ 202 | deep_release(rootHolder); 203 | } 204 | 205 | template 206 | void CBTree::publish(Node* ref){ 207 | hazard.publish(ref, Current[thread_num]); 208 | ++Current[thread_num]; 209 | } 210 | 211 | template 212 | void CBTree::releaseAll(){ 213 | for(unsigned int i = 0; i < Current[thread_num]; ++i){ 214 | hazard.release(i); 215 | } 216 | 217 | Current[thread_num] = 0; 218 | } 219 | 220 | template 221 | Node* CBTree::newNode(int key, bool value, Node* parent, long changeOVL, Node* left, Node* right){ 222 | Node* node = hazard.getFreeNode(); 223 | 224 | node->key = key; 225 | node->value = value; 226 | node->parent = parent; 227 | node->changeOVL = changeOVL; 228 | node->left = left; 229 | node->right = right; 230 | 231 | node->ncnt = 1; 232 | node->rcnt = 0; 233 | node->lcnt = 0; 234 | 235 | return node; 236 | } 237 | 238 | template 239 | bool CBTree::contains(T value){ 240 | int key = hash(value); 241 | 242 | while(true){ 243 | Node* right = rootHolder->right; 244 | 245 | if(!right){ 246 | return false; 247 | } else { 248 | int rightCmp = key - right->key; 249 | if(rightCmp == 0){ 250 | return right->value; 251 | } 252 | 253 | long ovl = right->changeOVL; 254 | if(isShrinkingOrUnlinked(ovl)){ 255 | right->waitUntilChangeCompleted(ovl); 256 | } else if(right == rootHolder->right){ 257 | Result vo = attemptGet(key, right, (rightCmp < 0 ? Left : Right), ovl, 1); 258 | if(vo != RETRY){ 259 | return vo == FOUND; 260 | } 261 | } 262 | } 263 | } 264 | } 265 | 266 | template 267 | Result CBTree::attemptGet(int key, Node* node, char dirToC, long nodeOVL, int height){ 268 | while(true){ 269 | Node* child = node->child(dirToC); 270 | 271 | if(!child){ 272 | if(hasShrunkOrUnlinked(nodeOVL, node->changeOVL)){ 273 | return RETRY; 274 | } 275 | 276 | return NOT_FOUND; 277 | } else { 278 | int childCmp = key - child->key; 279 | if(childCmp == 0){ 280 | int log_size = logSize.load(); 281 | 282 | if(height >= ((log_size << 2))){ 283 | SemiSplay(child); 284 | } else { 285 | RebalanceAtTarget(node, child); 286 | } 287 | 288 | ++child->ncnt; 289 | return child->value ? FOUND : NOT_FOUND; 290 | } 291 | 292 | long childOVL = child->changeOVL; 293 | if(isShrinkingOrUnlinked(childOVL)){ 294 | child->waitUntilChangeCompleted(childOVL); 295 | 296 | if(hasShrunkOrUnlinked(nodeOVL, node->changeOVL)){ 297 | return RETRY; 298 | } 299 | } else if(child != node->child(dirToC)){ 300 | if(hasShrunkOrUnlinked(nodeOVL, node->changeOVL)){ 301 | return RETRY; 302 | } 303 | } else { 304 | if(hasShrunkOrUnlinked(nodeOVL, node->changeOVL)){ 305 | return RETRY; 306 | } 307 | 308 | Result vo = attemptGet(key, child, (childCmp < 0 ? Left : Right), childOVL, height + 1); 309 | if(vo != RETRY){ 310 | if(vo != NOT_FOUND){ 311 | if(dirToC == Left){ 312 | ++node->lcnt; 313 | } else { 314 | ++node->rcnt; 315 | } 316 | } 317 | 318 | return vo; 319 | } 320 | } 321 | } 322 | } 323 | } 324 | 325 | template 326 | bool CBTree::add(T value){ 327 | if(update(hash(value)) == NOT_FOUND){ 328 | int log_size = logSize.load(); 329 | 330 | if(log_size < NEW_LOG_CALCULATION_THRESHOLD){ 331 | int new_size = (size += 1); 332 | int next_log_size = log_size + 1; 333 | if(new_size >= (1 << next_log_size)){ 334 | logSize.compare_exchange_strong(log_size, next_log_size); 335 | } 336 | } else { 337 | ++local_size[thread_num]; 338 | 339 | if(local_size[thread_num] >= Threads){ 340 | int new_size = (size += local_size[thread_num]); 341 | local_size[thread_num] = 0; 342 | int next_log_size = log_size + 1; 343 | if(new_size >= (1 << next_log_size)){ 344 | logSize.compare_exchange_strong(log_size, next_log_size); 345 | } 346 | } 347 | } 348 | 349 | return true; 350 | } else { 351 | return false; 352 | } 353 | } 354 | 355 | template 356 | bool CBTree::remove(T value){ 357 | int key = hash(value); 358 | 359 | while(true){ 360 | Node* right = rootHolder->right; 361 | 362 | if(!right){ 363 | releaseAll(); 364 | 365 | return false; 366 | } else { 367 | long ovl = right->changeOVL; 368 | if(isShrinkingOrUnlinked(ovl)){ 369 | right->waitUntilChangeCompleted(ovl); 370 | } else if (right == rootHolder->right){ 371 | Result vo = attemptRemove(key, rootHolder, right, ovl, 1); 372 | 373 | if(vo != RETRY){ 374 | if(vo == FOUND){ 375 | int log_size = logSize.load(); 376 | if(log_size < NEW_LOG_CALCULATION_THRESHOLD){ 377 | int new_size = (size -= 1); 378 | if(new_size < (1 << log_size)){ 379 | logSize.compare_exchange_strong(log_size, log_size - 1); 380 | } 381 | } else { 382 | --local_size[thread_num]; 383 | if(local_size[thread_num] <= -Threads){ 384 | int new_size = (size += local_size[thread_num]); 385 | local_size[thread_num] = 0; 386 | if(new_size < (1 << log_size)){ 387 | logSize.compare_exchange_strong(log_size, log_size - 1); 388 | } 389 | } 390 | } 391 | 392 | releaseAll(); 393 | 394 | return true; 395 | } else { 396 | releaseAll(); 397 | 398 | return false; 399 | } 400 | } 401 | } 402 | } 403 | } 404 | } 405 | 406 | template 407 | Result CBTree::update(int key){ 408 | while(true){ 409 | Node* right = rootHolder->right; 410 | 411 | if(!right){ 412 | if(attemptInsertIntoEmpty(key)){ 413 | return NOT_FOUND; 414 | } 415 | } else { 416 | long ovl = right->changeOVL; 417 | if(isShrinkingOrUnlinked(ovl)){ 418 | right->waitUntilChangeCompleted(ovl); 419 | } else if(right == rootHolder->right){ 420 | Result vo = attemptUpdate(key, rootHolder, right, ovl, 1); 421 | 422 | if(vo != RETRY){ 423 | return vo; 424 | } 425 | } 426 | } 427 | } 428 | } 429 | 430 | template 431 | bool CBTree::attemptInsertIntoEmpty(int key){ 432 | publish(rootHolder); 433 | scoped_lock lock(rootHolder->lock); 434 | 435 | if(!rootHolder->right){ 436 | rootHolder->right = newNode(key, true, rootHolder, 0L, nullptr, nullptr); 437 | releaseAll(); 438 | return true; 439 | } else { 440 | releaseAll(); 441 | return false; 442 | } 443 | } 444 | 445 | template 446 | Result CBTree::attemptUpdate(int key, Node* parent, Node* node, long nodeOVL, int height){ 447 | int cmp = key - node->key; 448 | if(cmp == 0){ 449 | int log_size = logSize.load(); 450 | 451 | if(height >= ((log_size << 2))){ 452 | SemiSplay(node); 453 | } else { 454 | RebalanceAtTarget(parent, node); 455 | } 456 | 457 | ++node->ncnt; 458 | return attemptNodeUpdate(true, parent, node); 459 | } 460 | 461 | char dirToC = cmp < 0 ? Left : Right; 462 | 463 | while(true){ 464 | Node* child = node->child(dirToC); 465 | 466 | if(hasShrunkOrUnlinked(nodeOVL, node->changeOVL)){ 467 | return RETRY; 468 | } 469 | 470 | if(!child){ 471 | bool doSemiSplay = false; 472 | 473 | { 474 | publish(node); 475 | scoped_lock lock(node->lock); 476 | 477 | if(hasShrunkOrUnlinked(nodeOVL, node->changeOVL)){ 478 | releaseAll(); 479 | return RETRY; 480 | } 481 | 482 | if(node->child(dirToC)){ 483 | //retry 484 | } else { 485 | node->setChild(dirToC, newNode(key, true, node, 0L, nullptr, nullptr)); 486 | 487 | if(dirToC == Left){ 488 | ++node->lcnt; 489 | } else { 490 | ++node->rcnt; 491 | } 492 | 493 | int log_size = logSize.load(); 494 | if(height >= ((log_size << 2 ))){ 495 | doSemiSplay = true; 496 | } else { 497 | releaseAll(); 498 | return NOT_FOUND; 499 | } 500 | } 501 | 502 | releaseAll(); 503 | } 504 | 505 | if(doSemiSplay){ 506 | SemiSplay(node->child(dirToC)); 507 | return NOT_FOUND; 508 | } 509 | } else { 510 | long childOVL = child->changeOVL; 511 | if(isShrinkingOrUnlinked(childOVL)){ 512 | child->waitUntilChangeCompleted(childOVL); 513 | } else if(child != node->child(dirToC)){ 514 | //Retry 515 | } else { 516 | if(hasShrunkOrUnlinked(nodeOVL, node->changeOVL)){ 517 | return RETRY; 518 | } 519 | 520 | Result vo = attemptUpdate(key, node, child, childOVL, height + 1); 521 | if(vo != RETRY){ 522 | if(vo == NOT_FOUND){ 523 | RebalanceNew(node, dirToC); 524 | } else { 525 | if(dirToC == Left){ 526 | ++node->lcnt; 527 | } else { 528 | ++node->rcnt; 529 | } 530 | } 531 | 532 | return vo; 533 | } 534 | } 535 | } 536 | } 537 | } 538 | 539 | template 540 | Result CBTree::attemptNodeUpdate(bool newValue, Node* parent, Node* node){ 541 | if(!newValue){ 542 | if(!node->value){ 543 | return NOT_FOUND; 544 | } 545 | } 546 | 547 | if(!newValue && (!node->left || !node->right)){ 548 | publish(parent); 549 | scoped_lock parentLock(parent->lock); 550 | 551 | if(isUnlinked(parent->changeOVL) || node->parent != parent){ 552 | releaseAll(); 553 | return RETRY; 554 | } 555 | 556 | publish(node); 557 | scoped_lock lock(node->lock); 558 | if(!node->value){ 559 | releaseAll(); 560 | return NOT_FOUND; 561 | } 562 | 563 | if(!attemptUnlink_nl(parent, node)){ 564 | releaseAll(); 565 | return RETRY; 566 | } 567 | 568 | //At this point, the parent can need unlink 569 | 570 | releaseAll(); 571 | 572 | return FOUND; 573 | } else { 574 | publish(node); 575 | scoped_lock lock(node->lock); 576 | 577 | if(isUnlinked(node->changeOVL)){ 578 | releaseAll(); 579 | return RETRY; 580 | } 581 | 582 | bool prev = node->value; 583 | 584 | if(!newValue && (!node->left || !node->right)){ 585 | releaseAll(); 586 | return RETRY; 587 | } 588 | 589 | node->value = newValue; 590 | 591 | releaseAll(); 592 | 593 | return prev ? FOUND : NOT_FOUND; 594 | } 595 | } 596 | 597 | template 598 | bool CBTree::attemptUnlink_nl(Node* parent, Node* node){ 599 | Node* parentL = parent->left; 600 | Node* parentR = parent->right; 601 | 602 | if(parentL != node && parentR != node){ 603 | return false; 604 | } 605 | 606 | Node* left = node->left; 607 | Node* right = node->right; 608 | if(left && right){ 609 | return false; 610 | } 611 | 612 | Node* splice = left ? left : right; 613 | 614 | if(parentL == node){ 615 | parent->left = splice; 616 | } else { 617 | parent->right = splice; 618 | } 619 | 620 | if(splice){ 621 | splice->parent = parent; 622 | } 623 | 624 | node->changeOVL = UnlinkedOVL; 625 | node->value = false; 626 | 627 | hazard.releaseNode(node); 628 | 629 | return true; 630 | } 631 | 632 | template 633 | Result CBTree::attemptRemove(int key, Node* parent, Node* node, long nodeOVL, int height){ 634 | int cmp = key - node->key; 635 | if(cmp == 0){ 636 | return attemptNodeUpdate(false, parent, node); 637 | } 638 | 639 | char dirToC = cmp < 0 ? Left : Right; 640 | 641 | while(true){ 642 | Node* child = node->child(dirToC); 643 | 644 | if(hasShrunkOrUnlinked(nodeOVL, node->changeOVL)){ 645 | return RETRY; 646 | } 647 | 648 | if(!child){ 649 | return NOT_FOUND; 650 | } else { 651 | long childOVL = child->changeOVL; 652 | 653 | if(isShrinkingOrUnlinked(childOVL)){ 654 | child->waitUntilChangeCompleted(childOVL); 655 | } else if(child != node->child(dirToC)){ 656 | //Retry 657 | } else { 658 | if(hasShrunkOrUnlinked(nodeOVL, node->changeOVL)){ 659 | return RETRY; 660 | } 661 | 662 | Result vo = attemptRemove(key, node, child, childOVL, height + 1); 663 | if(vo != RETRY){ 664 | return vo; 665 | } 666 | } 667 | } 668 | } 669 | } 670 | 671 | template 672 | void CBTree::SemiSplay(Node* child){ 673 | while(child && child->parent && child->parent->parent){ 674 | Node* node = child->parent; 675 | Node* parent = node->parent; 676 | 677 | if(!parent){ 678 | break; 679 | } 680 | 681 | if(!parent->parent){ 682 | publish(parent); 683 | scoped_lock parentLock(parent->lock); 684 | 685 | if(parent->right == node){ 686 | publish(node); 687 | scoped_lock nodeLock(node->lock); 688 | 689 | if(!isUnlinked(node->changeOVL)){ 690 | if(node->left == child){ 691 | publish(child); 692 | scoped_lock lock(child->lock); 693 | rotateRight(parent, node, child, child->right); 694 | } else if(node->right == child){ 695 | publish(child); 696 | scoped_lock lock(child->lock); 697 | rotateLeft(parent, node, child, child->left); 698 | } 699 | } 700 | } 701 | } else { 702 | Node* grand = parent->parent; 703 | publish(grand); 704 | scoped_lock lock(grand->lock); 705 | 706 | if(grand->left == parent || grand->right == parent){ 707 | publish(parent); 708 | scoped_lock parentLock(parent->lock); 709 | 710 | if(parent->left == node){ 711 | publish(node); 712 | scoped_lock nodeLock(node->lock); 713 | 714 | if(!isUnlinked(node->changeOVL)){ 715 | if(node->left == child){ 716 | publish(child); 717 | scoped_lock childLock(child->lock); 718 | rotateRight(grand, parent, node, node->right); 719 | child = node; 720 | } else if(node->right == child){ 721 | publish(child); 722 | scoped_lock childLock(child->lock); 723 | rotateRightOverLeft(grand, parent, node, child); 724 | } 725 | } 726 | } else if (parent->right == node){ 727 | publish(node); 728 | scoped_lock nodeLock(node->lock); 729 | 730 | if(!isUnlinked(node->changeOVL)){ 731 | if(node->right == child){ 732 | publish(child); 733 | scoped_lock childLock(child->lock); 734 | rotateLeft(grand, parent, node, node->left); 735 | child = node; 736 | } else if(node->left == child){ 737 | publish(child); 738 | scoped_lock childLock(child->lock); 739 | rotateLeftOverRight(grand, parent, node, child); 740 | } 741 | } 742 | } 743 | } 744 | } 745 | 746 | releaseAll(); 747 | } 748 | } 749 | 750 | template 751 | void CBTree::RebalanceAtTarget(Node* parent, Node* node){ 752 | int ncnt; 753 | int pcnt; 754 | int n_other_cnt; 755 | 756 | if(parent->left == node){ 757 | ncnt = node->ncnt + node->lcnt; 758 | pcnt = parent->ncnt + parent->rcnt; 759 | n_other_cnt = node->rcnt; 760 | } else { 761 | ncnt = node->ncnt + node->rcnt; 762 | pcnt = parent->ncnt + parent->lcnt; 763 | n_other_cnt = node->lcnt; 764 | } 765 | 766 | if(n_other_cnt >= pcnt){ 767 | Node* grand = parent->parent; 768 | publish(grand); 769 | scoped_lock grandLock(grand->lock); 770 | 771 | if(grand->left == parent || grand->right == parent){ 772 | publish(parent); 773 | scoped_lock parentLock(parent->lock); 774 | 775 | if(parent->left == node){ 776 | publish(node); 777 | scoped_lock nodeLock(node->lock); 778 | 779 | Node* nR = node->right; 780 | 781 | if(nR){ 782 | publish(nR); 783 | scoped_lock nRLock(nR->lock); 784 | 785 | rotateRightOverLeft(grand, parent, node, nR); 786 | parent->lcnt = nR->rcnt; 787 | node->rcnt = nR->lcnt; 788 | nR->rcnt += pcnt; 789 | nR->lcnt += ncnt; 790 | } 791 | } else if(parent->right == node){ 792 | publish(node); 793 | scoped_lock nodeLock(node->lock); 794 | 795 | Node* nL = node->left; 796 | 797 | if(nL){ 798 | publish(nL); 799 | scoped_lock nLLock(nL->lock); 800 | 801 | rotateLeftOverRight(grand, parent, node, nL); 802 | parent->rcnt = nL->lcnt; 803 | node->lcnt = nL->rcnt; 804 | nL->lcnt += pcnt; 805 | nL->rcnt += ncnt; 806 | } 807 | } 808 | } 809 | } else if(ncnt > pcnt){ 810 | Node* grand = parent->parent; 811 | publish(grand); 812 | scoped_lock grandLock(grand->lock); 813 | 814 | if(grand->left == parent || grand->right == parent){ 815 | publish(parent); 816 | scoped_lock parentLock(parent->lock); 817 | 818 | if(parent->left == node){ 819 | publish(node); 820 | scoped_lock nodeLock(node->lock); 821 | rotateRight(grand, parent, node, node->right); 822 | parent->lcnt = node->rcnt; 823 | node->rcnt += pcnt; 824 | } else if(parent->right == node){ 825 | publish(node); 826 | scoped_lock nodeLock(node->lock); 827 | rotateLeft(grand, parent, node, node->left); 828 | parent->rcnt = node->lcnt; 829 | node->lcnt += pcnt; 830 | } 831 | } 832 | } 833 | 834 | releaseAll(); 835 | } 836 | 837 | template 838 | void CBTree::RebalanceNew(Node* parent, char dirToC){ 839 | Node* node = parent->child(dirToC); 840 | int ncnt; 841 | int pcnt; 842 | int n_other_cnt; 843 | 844 | if(!node){ 845 | if(dirToC == Left){ 846 | ++parent->lcnt; 847 | } else { 848 | ++parent->rcnt; 849 | } 850 | return; 851 | } 852 | 853 | if(dirToC == Left){ 854 | ncnt = node->ncnt + node->lcnt; 855 | pcnt = parent->ncnt + parent->rcnt; 856 | n_other_cnt = node->rcnt; 857 | } else { 858 | ncnt = node->ncnt + node->rcnt; 859 | pcnt = parent->ncnt + parent->lcnt; 860 | n_other_cnt = node->lcnt; 861 | } 862 | 863 | if(n_other_cnt >= pcnt){ 864 | Node* grand = parent->parent; 865 | publish(grand); 866 | scoped_lock grandLock(grand->lock); 867 | 868 | if(grand->left == parent || grand->right == parent){ 869 | publish(parent); 870 | scoped_lock parentLock(parent->lock); 871 | 872 | if(parent->left == node){ 873 | publish(node); 874 | scoped_lock nodeLock(node->lock); 875 | 876 | Node* nR = node->right; 877 | if(nR){ 878 | publish(nR); 879 | scoped_lock nRLock(nR->lock); 880 | rotateRightOverLeft(grand, parent, node, nR); 881 | parent->lcnt = nR->rcnt; 882 | node->rcnt = nR->lcnt; 883 | nR->rcnt += pcnt; 884 | nR->lcnt += ncnt; 885 | } 886 | } else if(parent->right == node){ 887 | publish(node); 888 | scoped_lock nodeLock(node->lock); 889 | 890 | Node* nL = node->left; 891 | if(nL){ 892 | publish(nL); 893 | scoped_lock nLLock(nL->lock); 894 | rotateLeftOverRight(grand, parent, node, nL); 895 | parent->rcnt = nL->lcnt; 896 | node->lcnt = nL->rcnt; 897 | nL->lcnt += pcnt; 898 | nL->rcnt += ncnt; 899 | } 900 | } 901 | } 902 | } else if(ncnt > pcnt){ 903 | Node* grand = parent->parent; 904 | publish(grand); 905 | scoped_lock grandLock(grand->lock); 906 | if(grand->left == parent || grand->right == parent){ 907 | publish(parent); 908 | scoped_lock parentLock(parent->lock); 909 | if(parent->left == node){ 910 | publish(node); 911 | scoped_lock nodeLock(node->lock); 912 | rotateRight(grand, parent, node, node->right); 913 | parent->lcnt = node->rcnt; 914 | node->rcnt += pcnt; 915 | } else if(parent->right == node){ 916 | publish(node); 917 | scoped_lock nodeLock(node->lock); 918 | rotateLeft(grand, parent, node, node->left); 919 | parent->rcnt = node->lcnt; 920 | node->lcnt += pcnt; 921 | } 922 | } 923 | } else { 924 | if(dirToC == Left){ 925 | ++parent->lcnt; 926 | } else { 927 | ++parent->rcnt; 928 | } 929 | } 930 | 931 | releaseAll(); 932 | } 933 | 934 | template 935 | void CBTree::rotateRight(Node* nParent, Node* n, Node* nL, Node* nLR){ 936 | long nodeOVL = n->changeOVL; 937 | long leftOVL = nL->changeOVL; 938 | 939 | Node* nPL = nParent->left; 940 | 941 | n->changeOVL = beginShrink(nodeOVL); 942 | nL->changeOVL = beginGrow(leftOVL); 943 | 944 | n->left = nLR; 945 | nL->right = n; 946 | if(nPL == n){ 947 | nParent->left = nL; 948 | } else { 949 | nParent->right = nL; 950 | } 951 | 952 | nL->parent = nParent; 953 | n->parent = nL; 954 | if(nLR){ 955 | nLR->parent = n; 956 | } 957 | 958 | nL->changeOVL = endGrow(leftOVL); 959 | n->changeOVL = endShrink(nodeOVL); 960 | } 961 | 962 | template 963 | void CBTree::rotateLeft(Node* nParent, Node* n, Node* nR, Node* nRL){ 964 | long nodeOVL = n->changeOVL; 965 | long rightOVL = nR->changeOVL; 966 | 967 | Node* nPL = nParent->left; 968 | 969 | n->changeOVL = beginShrink(nodeOVL); 970 | nR->changeOVL = beginGrow(rightOVL); 971 | 972 | n->right = nRL; 973 | nR->left = n; 974 | 975 | if(nPL == n){ 976 | nParent->left = nR; 977 | } else { 978 | nParent->right = nR; 979 | } 980 | 981 | nR->parent = nParent; 982 | n->parent = nR; 983 | if(nRL){ 984 | nRL->parent = n; 985 | } 986 | 987 | nR->changeOVL = endGrow(rightOVL); 988 | n->changeOVL = endShrink(nodeOVL); 989 | } 990 | 991 | template 992 | void CBTree::rotateRightOverLeft(Node* nParent, Node* n, Node* nL, Node* nLR){ 993 | long nodeOVL = n->changeOVL; 994 | long leftOVL = nL->changeOVL; 995 | long leftROVL = nLR->changeOVL; 996 | 997 | Node* nPL = nParent->left; 998 | Node* nLRL = nLR->left; 999 | Node* nLRR = nLR->right; 1000 | 1001 | n->changeOVL = beginShrink(nodeOVL); 1002 | nL->changeOVL = beginShrink(leftOVL); 1003 | nLR->changeOVL = beginGrow(leftROVL); 1004 | 1005 | n->left = nLRR; 1006 | nL->right = nLRL; 1007 | nLR->left = nL; 1008 | nLR->right = n; 1009 | 1010 | if(nPL == n){ 1011 | nParent->left = nLR; 1012 | } else { 1013 | nParent->right = nLR; 1014 | } 1015 | 1016 | nLR->parent = nParent; 1017 | nL->parent = nLR; 1018 | n->parent = nLR; 1019 | 1020 | if(nLRR){ 1021 | nLRR->parent = n; 1022 | } 1023 | 1024 | if(nLRL){ 1025 | nLRL->parent = nL; 1026 | } 1027 | 1028 | nLR->changeOVL = endGrow(leftROVL); 1029 | nL->changeOVL = endShrink(leftOVL); 1030 | n->changeOVL = endShrink(nodeOVL); 1031 | } 1032 | 1033 | template 1034 | void CBTree::rotateLeftOverRight(Node* nParent, Node* n, Node* nR, Node* nRL){ 1035 | long nodeOVL = n->changeOVL; 1036 | long rightOVL = nR->changeOVL; 1037 | long rightLOVL = nRL->changeOVL; 1038 | 1039 | Node* nPL = nParent->left; 1040 | Node* nRLL = nRL->left; 1041 | Node* nRLR = nRL->right; 1042 | 1043 | n->changeOVL = beginShrink(nodeOVL); 1044 | nR->changeOVL = beginShrink(rightOVL); 1045 | nRL->changeOVL = beginGrow(rightLOVL); 1046 | 1047 | n->right = nRLL; 1048 | nR->left = nRLR; 1049 | nRL->right = nR; 1050 | nRL->left = n; 1051 | 1052 | if(nPL == n){ 1053 | nParent->left = nRL; 1054 | } else { 1055 | nParent->right = nRL; 1056 | } 1057 | 1058 | nRL->parent = nParent; 1059 | nR->parent = nRL; 1060 | n->parent = nRL; 1061 | 1062 | if(nRLL){ 1063 | nRLL->parent = n; 1064 | } 1065 | 1066 | if(nRLR){ 1067 | nRLR->parent = nR; 1068 | } 1069 | 1070 | nRL->changeOVL = endGrow(rightLOVL); 1071 | nR->changeOVL = endShrink(rightOVL); 1072 | n->changeOVL = endShrink(nodeOVL); 1073 | } 1074 | 1075 | } //end of cbtree 1076 | 1077 | #endif 1078 | -------------------------------------------------------------------------------- /include/lfmst/MultiwaySearchTree.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MULTIWAY_SEARCH_TREE 2 | #define MULTIWAY_SEARCH_TREE 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "hash.hpp" 10 | #include "Utils.hpp" 11 | #include "HazardManager.hpp" 12 | 13 | //Lock-Free Multiway Search Tree 14 | namespace lfmst { 15 | 16 | //Constants to manage array of search 17 | #define FIRST 6 18 | #define MAX 8 19 | 20 | struct Node; 21 | 22 | //In this data structure keys can be null or POSITIVE_INFINITY 23 | enum class KeyFlag : unsigned char { 24 | NORMAL, 25 | EMPTY, 26 | INF //POSITIVE_INFINITY 27 | }; 28 | 29 | struct Key { 30 | KeyFlag flag; 31 | int key; 32 | 33 | Key() : flag(KeyFlag::EMPTY), key(0) {} 34 | Key(KeyFlag flag, int key) : flag(flag), key(key) {} 35 | 36 | bool operator==(const Key& rhs){ 37 | return flag == rhs.flag && key == rhs.key; 38 | } 39 | 40 | bool operator!=(const Key& rhs){ 41 | return !(*this == rhs); 42 | } 43 | }; 44 | 45 | static void verify(int index, int length){ 46 | assert(index < length); 47 | } 48 | 49 | //Hold a set of key include the length of the set 50 | struct Keys { 51 | int length; 52 | Key* elements; 53 | 54 | Keys() : length(0), elements(nullptr) { 55 | //Nothing 56 | } 57 | 58 | ~Keys(){ 59 | if(elements){ 60 | free(elements); 61 | } 62 | } 63 | 64 | Key& operator[](int index){ 65 | verify(index, length); 66 | return elements[index]; 67 | } 68 | }; 69 | 70 | //Hold a set of key including the lenght of the set 71 | struct Children { 72 | int length; 73 | Node** elements; 74 | 75 | Children() : length(0), elements(nullptr) { 76 | //Nothing 77 | } 78 | 79 | ~Children(){ 80 | if(elements){ 81 | free(elements); 82 | } 83 | } 84 | 85 | Node*& operator[](int index){ 86 | verify(index, length); 87 | return elements[index]; 88 | } 89 | }; 90 | 91 | struct Contents { 92 | Keys* items; 93 | Children* children; 94 | Node* link; //The next node 95 | }; 96 | 97 | struct Node { 98 | Contents* contents; 99 | 100 | bool casContents(Contents* cts, Contents* newCts){ 101 | return CASPTR(&contents, cts, newCts); 102 | } 103 | }; 104 | 105 | struct Search { 106 | Node* node; 107 | Contents* contents; 108 | int index; 109 | }; 110 | 111 | struct HeadNode { 112 | Node* node; 113 | int height; 114 | }; 115 | 116 | template 117 | class MultiwaySearchTree { 118 | public: 119 | MultiwaySearchTree(); 120 | ~MultiwaySearchTree(); 121 | 122 | bool contains(T value); 123 | bool add(T value); 124 | bool remove(T value); 125 | 126 | private: 127 | HeadNode* root; 128 | 129 | int randomSeed; 130 | 131 | HazardManager roots; 132 | HazardManager nodes; 133 | HazardManager nodeContents; 134 | HazardManager nodeKeys; 135 | HazardManager nodeChildren; 136 | HazardManager searches; 137 | 138 | std::array, Threads> trash; 139 | 140 | HeadNode* newHeadNode(Node* node, int height); 141 | Search* newSearch(Node* node, Contents* contents, int index); 142 | Contents* newContents(Keys* items, Children* children, Node* link); 143 | Node* newNode(Contents* contents); 144 | Keys* newKeys(int length); 145 | Children* newChildren(int length); 146 | 147 | Keys* removeSingleItem(Keys* a, int index); 148 | Children* removeSingleItem(Children* a, int index); 149 | 150 | bool attemptSlideKey(Node* node, Contents* contents); 151 | bool shiftChild(Node* node, Contents* contents, int index, Node* adjustedChild); 152 | bool shiftChildren(Node* node, Contents* contents, Node* child1, Node* child2); 153 | bool dropChild(Node* node, Contents* contents, int index, Node* adjustedChild); 154 | bool slideToNeighbor(Node* sibling, Contents* sibContents, Key kkey, Key key, Node* child); 155 | Contents* deleteSlidedKey(Node* node, Contents* contents, Key key); 156 | Node* pushRight(Node* node, Key leftBarrier); 157 | 158 | Contents* cleanLink(Node* node, Contents* cts); 159 | void cleanNode(Key key, Node* node, Contents* cts, int index, Key leftBarrier); 160 | bool cleanNode1(Node* node, Contents* contents, Key leftBarrier); 161 | bool cleanNode2(Node* node, Contents* contents, Key leftBarrier); 162 | bool cleanNodeN(Node* node, Contents* contents, int index, Key leftBarrier); 163 | 164 | Search* traverseLeaf(Key key, bool cleanup); 165 | void traverseNonLeaf(Key key, int height, Search** results); 166 | Search* goodSamaritanCleanNeighbor(Key key, Search* results); 167 | bool removeFromNode(Key key, Search* results); 168 | 169 | Search* moveForward(Node* node, Key key, int hint); 170 | 171 | Keys* generateNewItems(Key key, Keys* keys, int index); 172 | Keys* generateLeftItems(Keys* children, int index); 173 | Keys* generateRightItems(Keys* children, int index); 174 | Children* generateNewChildren(Node* child, Children* children, int index); 175 | Children* generateLeftChildren(Children* children, int index); 176 | Children* generateRightChildren(Children* children, int index); 177 | Children* copyChildren(Children* rhs); 178 | 179 | //These methods can only be called from add 180 | char insertLeafLevel(Key key, Search* results, int length); 181 | bool beginInsertOneLevel(Key key, Search** results); 182 | Node* splitOneLevel(Key key, Search* result); 183 | void insertOneLevel(Key, Search** results, Node* right, int index); 184 | 185 | unsigned int randomLevel(); 186 | HeadNode* increaseRootHeight(int height); 187 | }; 188 | 189 | //Values for the random generation 190 | static const int avgLength = 32; 191 | static const int avgLengthMinusOne = 31; 192 | static const int logAvgLength = 5; // log_2 of the average node length 193 | 194 | template 195 | HeadNode* MultiwaySearchTree::newHeadNode(Node* node, int height){ 196 | HeadNode* root = roots.getFreeNode(); 197 | 198 | assert(root); 199 | 200 | root->node = node; 201 | root->height = height; 202 | 203 | return root; 204 | } 205 | 206 | template 207 | Search* MultiwaySearchTree::newSearch(Node* node, Contents* contents, int index){ 208 | Search* search = searches.getFreeNode(); 209 | 210 | assert(search); 211 | 212 | search->node = node; 213 | search->contents = contents; 214 | search->index = index; 215 | 216 | return search; 217 | } 218 | 219 | template 220 | Contents* MultiwaySearchTree::newContents(Keys* items, Children* children, Node* link){ 221 | Contents* contents = nodeContents.getFreeNode(); 222 | 223 | assert(contents); 224 | 225 | contents->items = items; 226 | contents->children = children; 227 | contents->link = link; 228 | 229 | return contents; 230 | } 231 | 232 | template 233 | Node* MultiwaySearchTree::newNode(Contents* contents){ 234 | Node* node = nodes.getFreeNode(); 235 | 236 | assert(node); 237 | 238 | node->contents = contents; 239 | 240 | return node; 241 | } 242 | 243 | template 244 | Keys* MultiwaySearchTree::newKeys(int length){ 245 | Keys* keys = nodeKeys.getFreeNode(); 246 | 247 | assert(keys); 248 | 249 | keys->length = length; 250 | 251 | //TODO A little optimization would be to keep the array if the sizes are the same (or even if the old length is greater) 252 | 253 | //If the object has already been used 254 | if(keys->elements){ 255 | free(keys->elements); 256 | } 257 | 258 | keys->elements = static_cast(calloc(length, sizeof(Key))); 259 | 260 | return keys; 261 | } 262 | 263 | template 264 | Children* MultiwaySearchTree::newChildren(int length){ 265 | Children* children = nodeChildren.getFreeNode(); 266 | 267 | assert(children); 268 | 269 | children->length = length; 270 | 271 | //TODO A little optimization would be to keep the array if the sizes are the same (or even if the old length is greater) 272 | 273 | //If the object has already been used 274 | if(children->elements){ 275 | free(children->elements); 276 | } 277 | 278 | children->elements = static_cast(calloc(length, sizeof(Node*))); 279 | 280 | return children; 281 | } 282 | 283 | /* Some internal utilities */ 284 | 285 | static int search(Keys* items, Key key); 286 | static int searchWithHint(Keys* items, Key key, int hint); 287 | static int compare(Key k1, Key k2); 288 | 289 | template 290 | Key special_hash(T value){ 291 | int key = hash(value); 292 | 293 | return {KeyFlag::NORMAL, key}; 294 | } 295 | 296 | template 297 | MultiwaySearchTree::MultiwaySearchTree(){ 298 | Keys* keys = newKeys(1); 299 | (*keys)[0] = {KeyFlag::INF, 0}; 300 | 301 | Contents* contents = newContents(keys, nullptr, nullptr); 302 | Node* node = newNode(contents); 303 | 304 | root = newHeadNode(node, 0); 305 | 306 | std::mt19937_64 engine(time(NULL)); 307 | std::uniform_int_distribution distribution(0, std::numeric_limits::max()); 308 | randomSeed = distribution(engine) | 0x0100; 309 | } 310 | 311 | template 312 | inline void transfer(std::list& source, std::unordered_set& target){ 313 | for(auto j : source){ 314 | target.insert(j); 315 | } 316 | source.clear(); 317 | } 318 | 319 | template 320 | inline void release_all(std::unordered_set& nodes, HP& hazard){ 321 | auto nodes_it = nodes.begin(); 322 | auto nodes_end = nodes.end(); 323 | 324 | while(nodes_it != nodes_end){ 325 | hazard.releaseNode(*nodes_it); 326 | 327 | ++nodes_it; 328 | } 329 | } 330 | 331 | template 332 | MultiwaySearchTree::~MultiwaySearchTree(){ 333 | std::unordered_set set_nodes; 334 | std::unordered_set set_keys; 335 | std::unordered_set set_contents; 336 | std::unordered_set set_children; 337 | 338 | //Get the trashed nodes 339 | for(unsigned int i = 0; i < Threads; ++i){ 340 | auto it = trash[i].begin(); 341 | auto end = trash[i].end(); 342 | 343 | while(it != end){ 344 | set_nodes.insert(*it); 345 | 346 | ++it; 347 | } 348 | } 349 | 350 | roots.releaseNode(root); 351 | 352 | std::vector stack; 353 | stack.push_back(root->node); 354 | 355 | while(stack.size() > 0){ 356 | Node* n = stack.back(); 357 | stack.pop_back(); 358 | 359 | if(n->contents){ 360 | if(n->contents->items){ 361 | set_keys.insert(n->contents->items); 362 | } 363 | 364 | if(n->contents->children){ 365 | for(int i = 0; i < n->contents->children->length; ++i){ 366 | stack.push_back((*n->contents->children)[i]); 367 | } 368 | 369 | set_children.insert(n->contents->children); 370 | } 371 | 372 | set_contents.insert(n->contents); 373 | 374 | n->contents = nullptr; 375 | } 376 | 377 | set_nodes.insert(n); 378 | } 379 | 380 | for(unsigned int i = 0; i < Threads; ++i){ 381 | transfer(nodes.direct_free(i), set_nodes); 382 | transfer(nodes.direct_local(i), set_nodes); 383 | 384 | transfer(nodeContents.direct_free(i), set_contents); 385 | transfer(nodeContents.direct_local(i), set_contents); 386 | 387 | transfer(nodeKeys.direct_free(i), set_keys); 388 | transfer(nodeKeys.direct_local(i), set_keys); 389 | 390 | transfer(nodeChildren.direct_free(i), set_children); 391 | transfer(nodeChildren.direct_local(i), set_children); 392 | } 393 | 394 | release_all(set_nodes, nodes); 395 | release_all(set_keys, nodeKeys); 396 | release_all(set_contents, nodeContents); 397 | release_all(set_children, nodeChildren); 398 | } 399 | 400 | template 401 | bool MultiwaySearchTree::contains(T value){ 402 | Key key = special_hash(value); 403 | 404 | Node* node = this->root->node; 405 | nodes.publish(node, 0); 406 | 407 | Contents* contents = node->contents; 408 | nodeContents.publish(contents, 0); 409 | nodeKeys.publish(contents->items, 0); 410 | nodeChildren.publish(contents->children, 0); 411 | 412 | int index = search(contents->items, key); 413 | while(contents->children){ 414 | if(-index -1 == contents->items->length){ 415 | node = contents->link; 416 | } else if(index < 0){ 417 | node = (*contents->children)[-index -1]; 418 | } else { 419 | node = (*contents->children)[index]; 420 | } 421 | 422 | nodes.publish(node, 0); 423 | 424 | contents = node->contents; 425 | nodeContents.publish(contents, 0); 426 | nodeKeys.publish(contents->items, 0); 427 | nodeChildren.publish(contents->children, 0); 428 | 429 | index = search(contents->items, key); 430 | } 431 | 432 | while(true){ 433 | if(-index - 1 == contents->items->length){ 434 | node = contents->link; 435 | } else { 436 | nodeContents.release(0); 437 | nodeKeys.release(0); 438 | nodeChildren.release(0); 439 | nodes.release(0); 440 | 441 | return index >= 0; 442 | } 443 | 444 | nodes.publish(node, 0); 445 | 446 | contents = node->contents; 447 | nodeContents.publish(contents, 0); 448 | nodeKeys.publish(contents->items, 0); 449 | nodeChildren.publish(contents->children, 0); 450 | 451 | index = search(contents->items, key); 452 | } 453 | } 454 | 455 | template 456 | bool MultiwaySearchTree::add(T value){ 457 | Key key = special_hash(value); 458 | 459 | unsigned int height = randomLevel(); 460 | if(height == 0){ 461 | Search* results = traverseLeaf(key, false); 462 | 463 | char inserted = insertLeafLevel(key, results, results->contents->items->length); 464 | 465 | //Retry 466 | if(inserted == 2){ 467 | return add(value); 468 | } 469 | 470 | //results is released in insertLeafLevel 471 | 472 | //Release references from traverseLeaf 473 | nodes.release(FIRST); 474 | nodeContents.release(FIRST); 475 | nodeKeys.release(FIRST); 476 | nodeChildren.release(FIRST); 477 | 478 | return inserted; 479 | } else { 480 | /* 481 | //Height debugging 482 | static int max_height = 0; 483 | if(height + 1 > max_height){ 484 | std::cout << height + 1 << std::endl; 485 | max_height = height + 1; 486 | } 487 | */ 488 | 489 | Search** results = static_cast(calloc(height + 1, sizeof(Search*))); 490 | traverseNonLeaf(key, height, results); 491 | 492 | bool inserted = beginInsertOneLevel(key, results); 493 | if(!inserted){ 494 | //Start at 1 because beginInsertOneLevel already cleaned the first index 495 | for(unsigned int i = 1; i < height + 1; ++i){ 496 | if(results[i]){ 497 | searches.releaseNode(results[i]); 498 | } 499 | } 500 | 501 | free(results); 502 | 503 | nodeContents.releaseAll(); 504 | nodeChildren.releaseAll(); 505 | nodeKeys.releaseAll(); 506 | 507 | return false; 508 | } 509 | 510 | for(unsigned int i = 0; i < height; ++i){ 511 | Node* right = splitOneLevel(key, results[i]); 512 | insertOneLevel(key, results, right, i + 1); 513 | } 514 | 515 | for(unsigned int i = 0; i < height + 1; ++i){ 516 | if(results[i]){ 517 | searches.releaseNode(results[i]); 518 | } 519 | } 520 | 521 | free(results); 522 | 523 | nodeContents.releaseAll(); 524 | nodeChildren.releaseAll(); 525 | nodeKeys.releaseAll(); 526 | 527 | return true; 528 | } 529 | } 530 | 531 | template 532 | Search* MultiwaySearchTree::traverseLeaf(Key key, bool cleanup){ 533 | Node* node = this->root->node; 534 | nodes.publish(node, 0); 535 | 536 | Contents* contents = node->contents; 537 | nodeContents.publish(contents, 0); 538 | nodeKeys.publish(contents->items, 0); 539 | nodeChildren.publish(contents->children, 0); 540 | 541 | int index = search(contents->items, key); 542 | Key leftBarrier = {KeyFlag::EMPTY, 0}; 543 | 544 | while(contents->children){ 545 | if(-index - 1 == contents->items->length){ 546 | if(contents->items->length > 0){ 547 | leftBarrier = (*contents->items)[contents->items->length - 1]; 548 | } 549 | 550 | node = cleanLink(node, contents)->link; 551 | } else { 552 | if(index < 0){ 553 | index = -index - 1; 554 | } 555 | 556 | if(cleanup){ 557 | cleanNode(key, node, contents, index, leftBarrier); 558 | } 559 | 560 | node = (*contents->children)[index]; 561 | leftBarrier = {KeyFlag::EMPTY, 0}; 562 | } 563 | 564 | nodes.publish(node, 0); 565 | 566 | contents = node->contents; 567 | nodeContents.publish(contents, 0); 568 | nodeKeys.publish(contents->items, 0); 569 | nodeChildren.publish(contents->children, 0); 570 | 571 | index = search(contents->items, key); 572 | } 573 | 574 | while(true){ 575 | if(index > -contents->items->length -1){ 576 | nodeContents.release(0); 577 | nodeKeys.release(0); 578 | nodeChildren.release(0); 579 | nodes.release(0); 580 | 581 | nodes.publish(node, FIRST); 582 | nodeContents.publish(contents, FIRST); 583 | nodeKeys.publish(contents->items, FIRST); 584 | nodeChildren.publish(contents->children, FIRST); 585 | 586 | return newSearch(node, contents, index); 587 | } else { 588 | node = cleanLink(node, contents)->link; 589 | } 590 | 591 | nodes.publish(node, 0); 592 | 593 | contents = node->contents; 594 | nodeContents.publish(contents, 0); 595 | nodeKeys.publish(contents->items, 0); 596 | nodeChildren.publish(contents->children, 0); 597 | 598 | index = search(contents->items, key); 599 | } 600 | } 601 | 602 | template 603 | void MultiwaySearchTree::traverseNonLeaf(Key key, int target, Search** storeResults){ 604 | HeadNode* root = this->root; 605 | 606 | if(root->height < target){ 607 | root = increaseRootHeight(target); 608 | } 609 | 610 | int height = root->height; 611 | Node* node = root->node; 612 | 613 | while(true){ 614 | nodes.publish(node, 0); 615 | 616 | Contents* contents = node->contents; 617 | nodeContents.publish(contents, 0); 618 | nodeKeys.publish(contents->items, 0); 619 | nodeChildren.publish(contents->children, 0); 620 | 621 | int index = search(contents->items, key); 622 | 623 | if(-index - 1 == contents->items->length){ 624 | node = contents->link; 625 | } else if(height == 0){ 626 | //Publish references to the contents contained in the Search 627 | nodes.publish(node, FIRST); 628 | nodeContents.publish(contents, FIRST); 629 | nodeKeys.publish(contents->items, FIRST); 630 | nodeChildren.publish(contents->children, FIRST); 631 | 632 | if(storeResults[0]){ 633 | searches.releaseNode(storeResults[0]); 634 | } 635 | 636 | storeResults[0] = newSearch(node, contents, index); 637 | 638 | nodeContents.release(0); 639 | nodeKeys.release(0); 640 | nodeChildren.release(0); 641 | nodes.release(0); 642 | 643 | return; 644 | } else { 645 | Search* first_results = newSearch(node, contents, index); 646 | Search* results = goodSamaritanCleanNeighbor(key, first_results); 647 | 648 | //Releases the references from the good samaritan 649 | nodeContents.release(2); 650 | nodeKeys.release(2); 651 | nodeChildren.release(2); 652 | 653 | if(results != first_results){ 654 | searches.releaseNode(first_results); 655 | } 656 | 657 | if(height <= target){ 658 | //Publish references to the contents contained in the Search 659 | nodes.publish(results->node, FIRST + height); 660 | nodeContents.publish(results->contents, FIRST + height); 661 | nodeKeys.publish(results->contents->items, FIRST + height); 662 | nodeChildren.publish(results->contents->children, FIRST + height); 663 | 664 | storeResults[height] = results; 665 | } else { 666 | searches.releaseNode(results); 667 | } 668 | 669 | if(index < 0){ 670 | index = -index - 1; 671 | } 672 | 673 | node = (*contents->children)[index]; 674 | height = height - 1; 675 | } 676 | } 677 | } 678 | 679 | template 680 | bool MultiwaySearchTree::remove(T value){ 681 | Key key = special_hash(value); 682 | 683 | Search* results = traverseLeaf(key, true); 684 | 685 | bool removed = removeFromNode(key, results); 686 | 687 | //results is released in removeFromNode 688 | 689 | //Release references from traverseLeaf 690 | nodes.release(FIRST); 691 | nodeContents.release(FIRST); 692 | nodeKeys.release(FIRST); 693 | nodeChildren.release(FIRST); 694 | 695 | return removed; 696 | } 697 | 698 | template 699 | bool MultiwaySearchTree::removeFromNode(Key key, Search* results){ 700 | while(true){ 701 | Node* node = results->node; 702 | Contents* contents = results->contents; 703 | 704 | int index = results->index; 705 | 706 | if(index < 0){ 707 | searches.releaseNode(results); 708 | 709 | return false; 710 | } else { 711 | nodes.publish(node, 0); 712 | nodeContents.publish(contents, 0); 713 | nodeKeys.publish(contents->items, 0); 714 | nodeChildren.publish(contents->children, 0); 715 | 716 | Keys* newKeys = removeSingleItem(contents->items, index); 717 | 718 | Contents* update = newContents(newKeys, nullptr, contents->link); 719 | 720 | //Note : contents->children is always empty here 721 | 722 | if(node->casContents(contents, update)){ 723 | nodeContents.releaseNode(contents); 724 | nodeChildren.releaseNode(contents->children); 725 | nodeKeys.releaseNode(contents->items); 726 | 727 | nodeContents.release(0); 728 | nodeKeys.release(0); 729 | nodeChildren.release(0); 730 | nodes.release(0); 731 | 732 | searches.releaseNode(results); 733 | 734 | return true; 735 | } else { 736 | nodeKeys.releaseNode(newKeys); 737 | nodeContents.releaseNode(update); 738 | 739 | nodeContents.release(0); 740 | nodeKeys.release(0); 741 | nodeChildren.release(0); 742 | nodes.release(0); 743 | 744 | searches.releaseNode(results); 745 | 746 | results = moveForward(node, key, index); 747 | } 748 | } 749 | } 750 | } 751 | 752 | //node must be published by parent 753 | template 754 | Contents* MultiwaySearchTree::cleanLink(Node* node, Contents* contents){ 755 | while(true){ 756 | nodeContents.publish(contents, 1); 757 | 758 | Node* newLink = pushRight(contents->link, {KeyFlag::EMPTY, 0}); 759 | 760 | if(newLink == contents->link){ 761 | nodeContents.release(1); 762 | nodeKeys.release(1); 763 | nodeChildren.release(1); 764 | 765 | return contents; 766 | } 767 | 768 | nodeKeys.publish(contents->items, 1); 769 | nodeChildren.publish(contents->children, 1); 770 | 771 | Contents* update = newContents(contents->items, contents->children, newLink); 772 | if(node->casContents(contents, update)){ 773 | nodeContents.releaseNode(contents); 774 | 775 | nodeContents.release(1); 776 | nodeKeys.release(1); 777 | nodeChildren.release(1); 778 | 779 | return update; 780 | } else { 781 | nodeContents.releaseNode(update); 782 | } 783 | 784 | nodeContents.release(1); 785 | nodeKeys.release(1); 786 | nodeChildren.release(1); 787 | 788 | contents = node->contents; 789 | } 790 | } 791 | 792 | int compare(Key k1, Key k2){ 793 | if(k1.flag == KeyFlag::INF){ 794 | return 1; 795 | } 796 | 797 | if(k2.flag == KeyFlag::INF){ 798 | return -1; 799 | } 800 | 801 | return k1.key - k2.key; 802 | } 803 | 804 | //node must be published by parent 805 | template 806 | void MultiwaySearchTree::cleanNode(Key key, Node* node, Contents* contents, int index, Key leftBarrier){ 807 | while(true){ 808 | nodeContents.publish(contents, 1); 809 | nodeKeys.publish(contents->items, 1); 810 | nodeChildren.publish(contents->children, 1); 811 | 812 | int length = contents->items->length; 813 | 814 | if(length == 0){ 815 | return; 816 | } else if(length == 1){ 817 | if(cleanNode1(node, contents, leftBarrier)){ 818 | nodeContents.release(1); 819 | nodeKeys.release(1); 820 | nodeChildren.release(1); 821 | 822 | //Note : It is not interesting to remove contents[0] here 823 | 824 | return; 825 | } 826 | } else if(length == 2){ 827 | if(cleanNode2(node, contents, leftBarrier)){ 828 | nodeContents.release(1); 829 | nodeKeys.release(1); 830 | nodeChildren.release(1); 831 | return; 832 | } 833 | } else { 834 | if(cleanNodeN(node, contents, index, leftBarrier)){ 835 | nodeContents.release(1); 836 | nodeKeys.release(1); 837 | nodeChildren.release(1); 838 | return; 839 | } 840 | } 841 | 842 | contents = node->contents; 843 | nodeContents.publish(contents, 1); 844 | nodeKeys.publish(contents->items, 1); 845 | nodeChildren.publish(contents->children, 1); 846 | 847 | index = search(contents->items, key); 848 | 849 | if(-index - 1 == contents->items->length){ 850 | nodeContents.release(1); 851 | nodeKeys.release(1); 852 | nodeChildren.release(1); 853 | return; 854 | } else if(index < 0){ 855 | index = -index -1; 856 | } 857 | } 858 | } 859 | 860 | //node must be published by parent 861 | //contents must be published 862 | //contents->items must be published 863 | //contents->children must be published 864 | template 865 | bool MultiwaySearchTree::cleanNode1(Node* node, Contents* contents, Key leftBarrier){ 866 | bool success = attemptSlideKey(node, contents); 867 | 868 | if(success){ 869 | return true; 870 | } 871 | 872 | Key key = (*contents->items)[0]; 873 | 874 | if(leftBarrier.flag != KeyFlag::EMPTY && compare(key, leftBarrier) <= 0){ 875 | leftBarrier = {KeyFlag::EMPTY, 0}; 876 | } 877 | 878 | Node* childNode = (*contents->children)[0]; 879 | Node* adjustedChild = pushRight(childNode, leftBarrier); 880 | 881 | if(adjustedChild == childNode){ 882 | return true; 883 | } 884 | 885 | //Note childNode cannot be released here 886 | return shiftChild(node, contents, 0, adjustedChild); 887 | } 888 | 889 | //node must be published by parent 890 | //contents must be published by parent 891 | //contents->items must be published 892 | //contents->children must be published 893 | template 894 | bool MultiwaySearchTree::cleanNode2(Node* node, Contents* contents, Key leftBarrier){ 895 | bool success = attemptSlideKey(node, contents); 896 | 897 | if(success){ 898 | return true; 899 | } 900 | 901 | Key key = (*contents->items)[0]; 902 | 903 | if(leftBarrier.flag != KeyFlag::EMPTY && compare(key, leftBarrier) <= 0){ 904 | leftBarrier = {KeyFlag::EMPTY, 0}; 905 | } 906 | 907 | Node* childNode1 = (*contents->children)[0]; 908 | Node* adjustedChild1 = pushRight(childNode1, leftBarrier); 909 | leftBarrier = (*contents->items)[0]; 910 | Node* childNode2 = (*contents->children)[1]; 911 | Node* adjustedChild2 = pushRight(childNode2, leftBarrier); 912 | 913 | if((adjustedChild1 == childNode1) && (adjustedChild2 == childNode2)){ 914 | return true; 915 | } 916 | 917 | //Note It is not a good place to release childNode1 and childNode2 918 | return shiftChildren(node, contents, adjustedChild1, adjustedChild2); 919 | } 920 | 921 | //node must be published by parent 922 | //contents must be published by parent 923 | //contents->items must be published 924 | //contents->children must be published 925 | template 926 | bool MultiwaySearchTree::cleanNodeN(Node* node, Contents* contents, int index, Key leftBarrier){ 927 | Key key0 = (*contents->items)[0]; 928 | 929 | if(index > 0){ 930 | leftBarrier = (*contents->items)[index - 1]; 931 | } else if(leftBarrier.flag != KeyFlag::EMPTY && compare(key0, leftBarrier) <= 0){ 932 | leftBarrier = {KeyFlag::EMPTY, 0}; 933 | } 934 | 935 | Node* childNode = (*contents->children)[index]; 936 | Node* adjustedChild = pushRight(childNode, leftBarrier); 937 | 938 | if(index == 0 || index == contents->children->length - 1){ 939 | if(adjustedChild == childNode){ 940 | return true; 941 | } 942 | 943 | //Note: childnode cannot be removed here 944 | return shiftChild(node, contents, index, adjustedChild); 945 | } 946 | 947 | Node* adjustedNeighbor = pushRight((*contents->children)[index + 1], (*contents->items)[index]); 948 | 949 | if(adjustedNeighbor == adjustedChild){ 950 | return dropChild(node, contents, index, adjustedChild); 951 | } else if(adjustedChild != childNode){ 952 | return shiftChild(node, contents, index, adjustedChild); 953 | } else { 954 | return true; 955 | } 956 | } 957 | 958 | template 959 | Node* MultiwaySearchTree::pushRight(Node* node, Key leftBarrier){ 960 | while(true){ 961 | nodes.publish(node, 0); 962 | 963 | Contents* contents = node->contents; 964 | nodeContents.publish(contents, 2); 965 | nodeKeys.publish(contents->items, 2); 966 | 967 | int length = contents->items->length; 968 | 969 | if(length == 0){ 970 | //It is a good idea to release contents->link afterward 971 | node = contents->link; 972 | trash[thread_num].push_back(node); 973 | } else if(leftBarrier.flag == KeyFlag::EMPTY || compare((*contents->items)[length - 1], leftBarrier) > 0){ 974 | nodeContents.release(2); 975 | nodeKeys.release(2); 976 | 977 | nodes.release(0); 978 | 979 | return node; 980 | } else { 981 | node = contents->link; 982 | } 983 | } 984 | } 985 | 986 | template 987 | unsigned int MultiwaySearchTree::randomLevel(){ 988 | unsigned int x = randomSeed; 989 | x ^= x << 13; 990 | x ^= x >> 17; 991 | randomSeed = x ^= x << 5; 992 | unsigned int level = 1; 993 | while ((x & avgLengthMinusOne) == 0) { 994 | if ((level % 6) == 0) { 995 | x = randomSeed; 996 | x ^= x << 13; 997 | x ^= x >> 17; 998 | randomSeed = x ^= x << 5; 999 | } else { 1000 | x >>= logAvgLength; 1001 | } 1002 | 1003 | level++; 1004 | } 1005 | 1006 | return (level - 1); 1007 | } 1008 | 1009 | //The Contents containing items should have been published 1010 | //items should have been published 1011 | int search(Keys* items, Key key){ 1012 | int low = 0; 1013 | int high = items->length - 1; 1014 | 1015 | if(low > high){ 1016 | return -1; 1017 | } 1018 | 1019 | if((*items)[high].flag == KeyFlag::INF){ 1020 | high--; 1021 | } 1022 | 1023 | while(low <= high){ 1024 | int mid = (low + high) >> 1; 1025 | Key midVal = (*items)[mid]; 1026 | 1027 | int cmp = compare(key, midVal); 1028 | if(cmp > 0){ 1029 | low = mid + 1; 1030 | } else if(cmp < 0){ 1031 | high = mid - 1; 1032 | } else { 1033 | return mid; 1034 | } 1035 | } 1036 | 1037 | return -(low + 1); //not found 1038 | } 1039 | 1040 | //The Contents containing items should have been published 1041 | //items should have been published 1042 | int searchWithHint(Keys* items, Key key, int hint){ 1043 | int low = 0; 1044 | int mid = hint; 1045 | int high = items->length - 1; 1046 | 1047 | if(low > high){ 1048 | return -1; 1049 | } 1050 | 1051 | if((*items)[high].flag == KeyFlag::INF){ 1052 | high--; 1053 | } 1054 | 1055 | if(mid > high){ 1056 | mid = (low + high) >> 1; 1057 | } 1058 | 1059 | while(low <= high){ 1060 | Key midVal = (*items)[mid]; 1061 | 1062 | int cmp = compare(key, midVal); 1063 | if(cmp > 0){ 1064 | low = mid + 1; 1065 | } else if(cmp < 0){ 1066 | high = mid - 1; 1067 | } else { 1068 | return mid; 1069 | } 1070 | 1071 | mid = (low + high) >> 1; 1072 | } 1073 | 1074 | return -(low + 1); //not found 1075 | } 1076 | 1077 | template 1078 | HeadNode* MultiwaySearchTree::increaseRootHeight(int target){ 1079 | HeadNode* root = this->root; 1080 | roots.publish(root, 0); 1081 | nodes.publish(root->node, 0); 1082 | 1083 | int height = root->height; 1084 | 1085 | while(height < target){ 1086 | Keys* keys = newKeys(1); 1087 | (*keys)[0].flag = KeyFlag::INF; 1088 | 1089 | Children* children = newChildren(1); 1090 | (*children)[0] = root->node; 1091 | 1092 | Contents* contents = newContents(keys, children, nullptr); 1093 | Node* newHeadNodeNode = newNode(contents); 1094 | HeadNode* update = newHeadNode(newHeadNodeNode, height + 1); 1095 | 1096 | if(CASPTR(&this->root, root, update)){ 1097 | roots.releaseNode(root); 1098 | } else { 1099 | nodeChildren.releaseNode(children); 1100 | nodeKeys.releaseNode(keys); 1101 | nodeContents.releaseNode(contents); 1102 | nodes.releaseNode(newHeadNodeNode); 1103 | roots.releaseNode(update); 1104 | } 1105 | 1106 | root = this->root; 1107 | roots.publish(root, 0); 1108 | nodes.publish(root->node, 0); 1109 | 1110 | height = root->height; 1111 | } 1112 | 1113 | roots.release(0); 1114 | nodes.release(0); 1115 | 1116 | return root; 1117 | } 1118 | 1119 | //node must be published by parent as 0 1120 | template 1121 | Search* MultiwaySearchTree::moveForward(Node* node, Key key, int hint){ 1122 | while(true){ 1123 | Contents* contents = node->contents; 1124 | nodeContents.publish(contents, 1); 1125 | nodeKeys.publish(contents->items, 1); 1126 | 1127 | int index = searchWithHint(contents->items, key, hint); 1128 | if(index > -contents->items->length - 1){ 1129 | nodeContents.release(1); 1130 | nodeKeys.release(1); 1131 | 1132 | return newSearch(node, contents, index); 1133 | } else { 1134 | node = contents->link; 1135 | 1136 | //erase the reference of the parent 1137 | nodes.publish(node, 0); 1138 | } 1139 | } 1140 | } 1141 | 1142 | //node must be published by parent 1143 | //contents must be published by parent 1144 | //contents->items must be published 1145 | //contents->children must be published 1146 | template 1147 | bool MultiwaySearchTree::shiftChild(Node* node, Contents* contents, int index, Node* adjustedChild){ 1148 | Children* children = copyChildren(contents->children); 1149 | (*children)[index] = adjustedChild; 1150 | 1151 | Contents* update = newContents(contents->items, children, contents->link); 1152 | if(node->casContents(contents, update)){ 1153 | nodeContents.releaseNode(contents); 1154 | nodeChildren.releaseNode(contents->children); 1155 | 1156 | return true; 1157 | } else { 1158 | nodeChildren.releaseNode(children); 1159 | nodeContents.releaseNode(update); 1160 | 1161 | return false; 1162 | } 1163 | } 1164 | 1165 | //node must be published by parent 1166 | //contents must be published by parent 1167 | //contents->items must be published 1168 | //contents->children must be published 1169 | template 1170 | bool MultiwaySearchTree::shiftChildren(Node* node, Contents* contents, Node* child1, Node* child2){ 1171 | Children* children = newChildren(2); 1172 | (*children)[0] = child1; 1173 | (*children)[1] = child2; 1174 | 1175 | Contents* update = newContents(contents->items, children, contents->link); 1176 | if(node->casContents(contents, update)){ 1177 | nodeContents.releaseNode(contents); 1178 | nodeChildren.releaseNode(contents->children); 1179 | 1180 | return true; 1181 | } else { 1182 | nodeChildren.releaseNode(children); 1183 | nodeContents.releaseNode(update); 1184 | 1185 | return false; 1186 | } 1187 | } 1188 | 1189 | //node must be published by parent 1190 | //contents must be published by parent 1191 | //contents->children must be published 1192 | //contents->item must be published 1193 | template 1194 | bool MultiwaySearchTree::dropChild(Node* node, Contents* contents, int index, Node* adjustedChild){ 1195 | int length = contents->items->length; 1196 | 1197 | Keys* keys = newKeys(length - 1); 1198 | Children* children = newChildren(length - 1); 1199 | 1200 | for(int i = 0; i < index; ++i){ 1201 | (*keys)[i] = (*contents->items)[i]; 1202 | (*children)[i] = (*contents->children)[i]; 1203 | } 1204 | 1205 | (*children)[index] = adjustedChild; 1206 | 1207 | for(int i = index + 1; i < length; ++i){ 1208 | (*keys)[i - 1] = (*contents->items)[i]; 1209 | } 1210 | 1211 | for(int i = index + 2; i < length; ++i){ 1212 | (*children)[i - 1] = (*contents->children)[i]; 1213 | } 1214 | 1215 | Contents* update = newContents(keys, children, contents->link); 1216 | if(node->casContents(contents, update)){ 1217 | nodeContents.releaseNode(contents); 1218 | nodeChildren.releaseNode(contents->children); 1219 | nodeKeys.releaseNode(contents->items); 1220 | 1221 | return true; 1222 | } else { 1223 | nodeChildren.releaseNode(children); 1224 | nodeKeys.releaseNode(keys); 1225 | nodeContents.releaseNode(update); 1226 | 1227 | return false; 1228 | } 1229 | } 1230 | 1231 | //node must be published by parent 1232 | //contents is published by parent 1233 | //contents->items is published 1234 | //contents->children is published 1235 | template 1236 | bool MultiwaySearchTree::attemptSlideKey(Node* node, Contents* contents){ 1237 | if(!contents->link){ 1238 | return false; 1239 | } 1240 | 1241 | int length = contents->items->length; 1242 | Key kkey = (*contents->items)[length - 1]; 1243 | 1244 | Node* child = (*contents->children)[length - 1]; 1245 | nodes.publish(child, 2); 1246 | 1247 | Node* sibling = pushRight(contents->link, {KeyFlag::EMPTY, 0}); 1248 | nodes.publish(sibling, 3); 1249 | 1250 | Contents* siblingContents = sibling->contents; 1251 | nodeContents.publish(siblingContents, 2); 1252 | nodeKeys.publish(siblingContents->items, 2); 1253 | nodeChildren.publish(siblingContents->children, 2); 1254 | 1255 | Node* nephew = nullptr; 1256 | if(siblingContents->children->length == 0){ 1257 | nodeContents.release(2); 1258 | nodeKeys.release(2); 1259 | nodeChildren.release(2); 1260 | 1261 | nodes.release(2); 1262 | nodes.release(3); 1263 | 1264 | return false; 1265 | } else { 1266 | nephew = (*siblingContents->children)[0]; 1267 | nodes.publish(nephew, 1); 1268 | } 1269 | 1270 | if(compare((*siblingContents->items)[0], kkey) > 0){ 1271 | nephew = pushRight(nephew, kkey); 1272 | nodes.publish(nephew, 1); 1273 | } else { 1274 | nephew = pushRight(nephew, {KeyFlag::EMPTY, 0}); 1275 | nodes.publish(nephew, 1); 1276 | } 1277 | 1278 | if(nephew != child){ 1279 | nodeContents.release(2); 1280 | nodeKeys.release(2); 1281 | nodeChildren.release(2); 1282 | 1283 | nodes.release(1); 1284 | nodes.release(2); 1285 | nodes.release(3); 1286 | 1287 | return false; 1288 | } 1289 | 1290 | bool success = slideToNeighbor(sibling, siblingContents, kkey, kkey, child); 1291 | if(success){ 1292 | deleteSlidedKey(node, contents, kkey); 1293 | } 1294 | 1295 | //Note: oldNephew cannot be released here 1296 | //Note: oldLink cannot be released here 1297 | 1298 | nodeContents.release(2); 1299 | nodeKeys.release(2); 1300 | nodeChildren.release(2); 1301 | 1302 | nodes.release(1); 1303 | nodes.release(2); 1304 | nodes.release(3); 1305 | 1306 | return true; 1307 | } 1308 | 1309 | //sibling must be published by parent 1310 | //sibContents is published by parent 1311 | //sibContents->items is published 1312 | //sibContents->children is published 1313 | template 1314 | bool MultiwaySearchTree::slideToNeighbor(Node* sibling, Contents* sibContents, Key kkey, Key key, Node* child){ 1315 | int index = search(sibContents->items, key); 1316 | if(index >= 0){ 1317 | return true; 1318 | } else if(index < -1){ 1319 | return false; 1320 | } 1321 | 1322 | Keys* keys = generateNewItems(kkey, sibContents->items, 0); 1323 | Children* children = generateNewChildren(child, sibContents->children, 0); 1324 | 1325 | Contents* update = newContents(keys, children, sibContents->link); 1326 | if(sibling->casContents(sibContents, update)){ 1327 | nodeContents.releaseNode(sibContents); 1328 | nodeKeys.releaseNode(sibContents->items); 1329 | nodeChildren.releaseNode(sibContents->children); 1330 | 1331 | return true; 1332 | } else { 1333 | nodeKeys.releaseNode(keys); 1334 | nodeChildren.releaseNode(children); 1335 | nodeContents.releaseNode(update); 1336 | 1337 | return false; 1338 | } 1339 | } 1340 | 1341 | //node must be published by parent 1342 | //contents is published 1343 | //contents->items is published 1344 | //contents->children is published 1345 | template 1346 | Contents* MultiwaySearchTree::deleteSlidedKey(Node* node, Contents* contents, Key key){ 1347 | int index = search(contents->items, key); 1348 | if(index < 0){ 1349 | return contents; 1350 | } 1351 | 1352 | Keys* keys = removeSingleItem(contents->items, index); 1353 | Children* children = removeSingleItem(contents->children, index); 1354 | 1355 | Contents* update = newContents(keys, children, contents->link); 1356 | if(node->casContents(contents, update)){ 1357 | nodeContents.releaseNode(contents); 1358 | nodeChildren.releaseNode(contents->children); 1359 | nodeKeys.releaseNode(contents->items); 1360 | 1361 | //contents->children[index] cannot be released here 1362 | 1363 | return update; 1364 | } else { 1365 | nodeKeys.releaseNode(keys); 1366 | nodeChildren.releaseNode(children); 1367 | nodeContents.releaseNode(update); 1368 | 1369 | return contents; 1370 | } 1371 | } 1372 | 1373 | template 1374 | Search* MultiwaySearchTree::goodSamaritanCleanNeighbor(Key key, Search* results){ 1375 | Node* node = results->node; 1376 | nodes.publish(node, 1); 1377 | 1378 | Contents* contents = results->contents; 1379 | nodeContents.publish(contents, 2); 1380 | 1381 | if(!contents->link || !contents->items || !contents->items){ //That's ugly 1382 | nodeContents.release(2); 1383 | nodes.release(1); 1384 | 1385 | return results; 1386 | } 1387 | 1388 | nodeKeys.publish(contents->items, 2); 1389 | nodeChildren.publish(contents->children, 2); 1390 | 1391 | int length = contents->items->length; 1392 | Key leftBarrier = (*contents->items)[length - 1]; 1393 | Node* child = (*contents->children)[length - 1]; 1394 | nodes.publish(child, 2); 1395 | 1396 | Node* sibling = pushRight(contents->link, {KeyFlag::EMPTY, 0}); 1397 | nodes.publish(sibling, 3); 1398 | 1399 | Contents* siblingContents = sibling->contents; 1400 | nodeContents.publish(siblingContents, 3); 1401 | nodeKeys.publish(siblingContents->items, 3); 1402 | nodeChildren.publish(siblingContents->children, 3); 1403 | 1404 | Node* nephew = nullptr; 1405 | Node* adjustedNephew = nullptr; 1406 | 1407 | if(siblingContents->children->length == 0){ 1408 | contents = cleanLink(node, node->contents); 1409 | int index = search(contents->items, key); 1410 | 1411 | nodeContents.release(3); 1412 | nodeKeys.release(3); 1413 | nodeChildren.release(3); 1414 | 1415 | nodes.release(1); 1416 | nodes.release(2); 1417 | nodes.release(3); 1418 | 1419 | //References 2 are released by the caller 1420 | return newSearch(node, contents, index); 1421 | } else { 1422 | nephew = (*siblingContents->children)[0]; 1423 | nodes.publish(nephew, 4); 1424 | } 1425 | 1426 | if(compare((*siblingContents->items)[0], leftBarrier) > 0){ 1427 | adjustedNephew = pushRight(nephew, leftBarrier); 1428 | nodes.publish(adjustedNephew, 5); 1429 | } else { 1430 | adjustedNephew = pushRight(nephew, {KeyFlag::EMPTY, 0}); 1431 | nodes.publish(adjustedNephew, 5); 1432 | } 1433 | 1434 | if(nephew != child){ 1435 | if(adjustedNephew != nephew){ 1436 | shiftChild(sibling, siblingContents, 0, adjustedNephew); 1437 | } 1438 | } else { 1439 | bool success = slideToNeighbor(sibling, siblingContents, leftBarrier, leftBarrier, child); 1440 | if(success){ 1441 | contents = deleteSlidedKey(node, contents, leftBarrier); 1442 | nodeContents.publish(contents, 2); 1443 | nodeKeys.publish(contents->items, 2); 1444 | nodeChildren.publish(contents->children, 2); 1445 | 1446 | int index = search(contents->items, key); 1447 | 1448 | nodeContents.release(3); 1449 | nodeKeys.release(3); 1450 | nodeChildren.release(3); 1451 | 1452 | nodes.release(1); 1453 | nodes.release(2); 1454 | nodes.release(3); 1455 | nodes.release(4); 1456 | nodes.release(5); 1457 | 1458 | //References 2 are released by the caller 1459 | return newSearch(node, contents, index); 1460 | } 1461 | } 1462 | 1463 | nodeContents.release(2); 1464 | nodeKeys.release(2); 1465 | nodeChildren.release(2); 1466 | nodeContents.release(3); 1467 | nodeKeys.release(3); 1468 | nodeChildren.release(3); 1469 | 1470 | nodes.release(1); 1471 | nodes.release(2); 1472 | nodes.release(3); 1473 | nodes.release(4); 1474 | nodes.release(5); 1475 | 1476 | return results; 1477 | } 1478 | 1479 | template 1480 | Node* MultiwaySearchTree::splitOneLevel(Key key, Search* results){ 1481 | Search* entry_results = results; 1482 | 1483 | while(true){ 1484 | Node* node = results->node; 1485 | nodes.publish(node, 0); 1486 | 1487 | Contents* contents = results->contents; 1488 | nodeContents.publish(contents, 0); 1489 | nodeKeys.publish(contents->items, 0); 1490 | nodeChildren.publish(contents->children, 0); 1491 | 1492 | int index = results->index; 1493 | int length = contents->items->length; 1494 | 1495 | if(index < 0){ 1496 | nodeContents.release(0); 1497 | nodeKeys.release(0); 1498 | nodeChildren.release(0); 1499 | nodes.release(0); 1500 | 1501 | if(results != entry_results){ 1502 | searches.releaseNode(results); 1503 | } 1504 | 1505 | return nullptr; 1506 | } else if(length < 2 || index == (length - 1)){ 1507 | nodeContents.release(0); 1508 | nodeKeys.release(0); 1509 | nodeChildren.release(0); 1510 | nodes.release(0); 1511 | 1512 | if(results != entry_results){ 1513 | searches.releaseNode(results); 1514 | } 1515 | 1516 | return nullptr; 1517 | } 1518 | 1519 | Keys* leftKeys = generateLeftItems(contents->items, index); 1520 | Keys* rightKeys = generateRightItems(contents->items, index); 1521 | Children* leftChildren = generateLeftChildren(contents->children, index); 1522 | Children* rightChildren = generateRightChildren(contents->children, index); 1523 | 1524 | Contents* rightContents = newContents(rightKeys, rightChildren, contents->link); 1525 | Node* right = newNode(rightContents); 1526 | Contents* left = newContents(leftKeys, leftChildren, right); 1527 | 1528 | if(node->casContents(contents, left)){ 1529 | nodeContents.releaseNode(contents); 1530 | nodeChildren.releaseNode(contents->children); 1531 | nodeKeys.releaseNode(contents->items); 1532 | 1533 | nodeContents.release(0); 1534 | nodeKeys.release(0); 1535 | nodeChildren.release(0); 1536 | nodes.release(0); 1537 | 1538 | if(results != entry_results){ 1539 | searches.releaseNode(results); 1540 | } 1541 | 1542 | return right; 1543 | } else { 1544 | nodeKeys.releaseNode(leftKeys); 1545 | nodeKeys.releaseNode(rightKeys); 1546 | nodeChildren.releaseNode(leftChildren); 1547 | nodeChildren.releaseNode(rightChildren); 1548 | 1549 | nodeContents.releaseNode(rightContents); 1550 | nodes.releaseNode(right); 1551 | nodeContents.releaseNode(left); 1552 | 1553 | if(results != entry_results){ 1554 | searches.releaseNode(results); 1555 | } 1556 | 1557 | results = moveForward(node, key, index); 1558 | } 1559 | 1560 | nodeContents.release(0); 1561 | nodeKeys.release(0); 1562 | nodeChildren.release(0); 1563 | nodes.release(0); 1564 | } 1565 | } 1566 | 1567 | template 1568 | char MultiwaySearchTree::insertLeafLevel(Key key, Search* results, int back){ 1569 | int back_length = back; 1570 | 1571 | while(true){ 1572 | Node* node = results->node; 1573 | nodes.publish(node, 0); 1574 | 1575 | Contents* contents = results->contents; 1576 | nodeContents.publish(contents, 0); 1577 | nodeChildren.publish(contents->children, 0); 1578 | 1579 | Keys* keys = contents->items; 1580 | nodeKeys.publish(keys, 0); 1581 | 1582 | int index = results->index; 1583 | 1584 | if(index >= 0){ 1585 | nodes.release(0); 1586 | nodeContents.release(0); 1587 | nodeKeys.release(0); 1588 | nodeChildren.release(0); 1589 | 1590 | searches.releaseNode(results); 1591 | 1592 | return false; 1593 | } else { 1594 | index = -index - 1; 1595 | 1596 | if(keys->length != back_length || index >= back_length){ 1597 | return 2; //RETRY 1598 | } 1599 | 1600 | Keys* newKeys = generateNewItems(key, keys, index); 1601 | 1602 | Contents* update = newContents(newKeys, nullptr, contents->link); 1603 | if(node->casContents(contents, update)){ 1604 | nodeContents.releaseNode(contents); 1605 | nodeChildren.releaseNode(contents->children); 1606 | nodeKeys.releaseNode(contents->items); 1607 | 1608 | nodes.release(0); 1609 | nodeContents.release(0); 1610 | nodeKeys.release(0); 1611 | nodeChildren.release(0); 1612 | 1613 | searches.releaseNode(results); 1614 | 1615 | return true; 1616 | } else { 1617 | nodeKeys.releaseNode(newKeys); 1618 | nodeContents.releaseNode(update); 1619 | 1620 | searches.releaseNode(results); 1621 | 1622 | results = moveForward(node, key, index); 1623 | 1624 | back_length = results->contents->items->length; 1625 | } 1626 | 1627 | nodes.release(0); 1628 | nodeContents.release(0); 1629 | nodeKeys.release(0); 1630 | nodeChildren.release(0); 1631 | } 1632 | } 1633 | } 1634 | 1635 | template 1636 | bool MultiwaySearchTree::beginInsertOneLevel(Key key, Search** resultsStore){ 1637 | Search* results = resultsStore[0]; 1638 | 1639 | while(true){ 1640 | Node* node = results->node; 1641 | nodes.publish(node, 0); 1642 | 1643 | Contents* contents = results->contents; 1644 | nodeContents.publish(contents, 0); 1645 | nodeKeys.publish(contents->items, 0); 1646 | nodeChildren.publish(contents->children, 0); 1647 | 1648 | int index = results->index; 1649 | Keys* keys = contents->items; 1650 | 1651 | if(index >= 0){ 1652 | nodeContents.release(0); 1653 | nodeKeys.release(0); 1654 | nodeChildren.release(0); 1655 | nodes.release(0); 1656 | 1657 | //If we return false, the value in resultsStore will never be used 1658 | searches.releaseNode(results); 1659 | 1660 | return false; 1661 | } else { 1662 | index = -index - 1; 1663 | 1664 | Keys* newKeys = generateNewItems(key, keys, index); 1665 | 1666 | Contents* update = newContents(newKeys, nullptr, contents->link); 1667 | if(node->casContents(contents, update)){ 1668 | nodeContents.releaseNode(contents); 1669 | nodeChildren.releaseNode(contents->children); 1670 | nodeKeys.releaseNode(contents->items); 1671 | 1672 | nodeContents.release(0); 1673 | nodeKeys.release(0); 1674 | nodeChildren.release(0); 1675 | nodes.release(0); 1676 | 1677 | searches.releaseNode(results); 1678 | 1679 | //Publish references to the contents contained in the Search 1680 | nodes.publish(node, FIRST); 1681 | nodeContents.publish(update, FIRST); 1682 | nodeKeys.publish(update->items, FIRST); 1683 | nodeChildren.publish(update->children, FIRST); 1684 | 1685 | resultsStore[0] = newSearch(node, update, index); 1686 | 1687 | return true; 1688 | } else { 1689 | nodeKeys.releaseNode(newKeys); 1690 | nodeContents.releaseNode(update); 1691 | 1692 | searches.releaseNode(results); 1693 | 1694 | results = moveForward(node, key, index); 1695 | } 1696 | 1697 | nodeContents.release(0); 1698 | nodeKeys.release(0); 1699 | nodeChildren.release(0); 1700 | nodes.release(0); 1701 | } 1702 | } 1703 | } 1704 | 1705 | template 1706 | void MultiwaySearchTree::insertOneLevel(Key key, Search** resultsStore, Node* child, int target){ 1707 | if(!child){ 1708 | return; 1709 | } 1710 | 1711 | Search* results = resultsStore[target]; 1712 | Search* entry_results = results; 1713 | 1714 | while(true){ 1715 | Node* node = results->node; 1716 | nodes.publish(node, 0); 1717 | 1718 | Contents* contents = results->contents; 1719 | nodeContents.publish(contents, 0); 1720 | nodeKeys.publish(contents->items, 0); 1721 | nodeChildren.publish(contents->children, 0); 1722 | 1723 | int index = results->index; 1724 | 1725 | if(index >= 0){ 1726 | if(results != entry_results){ 1727 | searches.releaseNode(results); 1728 | } 1729 | 1730 | nodes.release(0); 1731 | nodeContents.release(0); 1732 | nodeKeys.release(0); 1733 | nodeChildren.release(0); 1734 | 1735 | return; 1736 | } else if(index > -contents->items->length - 1){ 1737 | index = -index -1; 1738 | 1739 | Keys* newKeys = generateNewItems(key, contents->items, index); 1740 | Children* newChildren = generateNewChildren(child, contents->children, index + 1); 1741 | 1742 | Contents* update = newContents(newKeys, newChildren, contents->link); 1743 | if(node->casContents(contents, update)){ 1744 | if(results != entry_results){ 1745 | searches.releaseNode(results); 1746 | } 1747 | 1748 | nodeContents.releaseNode(contents); 1749 | nodeChildren.releaseNode(contents->children); 1750 | nodeKeys.releaseNode(contents->items); 1751 | 1752 | nodes.release(0); 1753 | nodeContents.release(0); 1754 | nodeKeys.release(0); 1755 | nodeChildren.release(0); 1756 | 1757 | searches.releaseNode(resultsStore[target]); 1758 | 1759 | //Publish references to the contents contained in the Search 1760 | nodes.publish(node, FIRST + target); 1761 | nodeContents.publish(update, FIRST + target); 1762 | nodeKeys.publish(update->items, FIRST + target); 1763 | nodeChildren.publish(update->children, FIRST + target); 1764 | 1765 | resultsStore[target] = newSearch(node, update, index); 1766 | 1767 | return; 1768 | } else { 1769 | nodeKeys.releaseNode(newKeys); 1770 | nodeChildren.releaseNode(newChildren); 1771 | nodeContents.releaseNode(update); 1772 | 1773 | if(results != entry_results){ 1774 | searches.releaseNode(results); 1775 | } 1776 | 1777 | results = moveForward(node, key, index); 1778 | } 1779 | } else { 1780 | if(results != entry_results){ 1781 | searches.releaseNode(results); 1782 | } 1783 | 1784 | results = moveForward(node, key, -index - 1); 1785 | } 1786 | 1787 | nodes.release(0); 1788 | nodeContents.release(0); 1789 | nodeKeys.release(0); 1790 | nodeChildren.release(0); 1791 | } 1792 | } 1793 | 1794 | /* Utility methods to manipulate arrays */ 1795 | 1796 | template 1797 | Children* MultiwaySearchTree::copyChildren(Children* rhs){ 1798 | Children* copy = newChildren(rhs->length); 1799 | 1800 | for(int i = 0; i < copy->length; ++i){ 1801 | (*copy)[i] = (*rhs)[i]; 1802 | } 1803 | 1804 | return copy; 1805 | } 1806 | 1807 | template 1808 | Keys* MultiwaySearchTree::removeSingleItem(Keys* a, int index){ 1809 | int length = a->length; 1810 | Keys* newArray = newKeys(length - 1); 1811 | 1812 | for(int i = 0; i < index; ++i){ 1813 | (*newArray)[i] = (*a)[i]; 1814 | } 1815 | 1816 | for(int i = index + 1; i < length; ++i){ 1817 | (*newArray)[i - 1] = (*a)[i]; 1818 | } 1819 | 1820 | return newArray; 1821 | } 1822 | 1823 | template 1824 | Children* MultiwaySearchTree::removeSingleItem(Children* a, int index){ 1825 | int length = a->length; 1826 | Children* newArray = newChildren(length - 1); 1827 | 1828 | for(int i = 0; i < index; ++i){ 1829 | (*newArray)[i] = (*a)[i]; 1830 | } 1831 | 1832 | for(int i = index + 1; i < length; ++i){ 1833 | (*newArray)[i - 1] = (*a)[i]; 1834 | } 1835 | 1836 | return newArray; 1837 | } 1838 | 1839 | template 1840 | Keys* MultiwaySearchTree::generateNewItems(Key key, Keys* items, int index){ 1841 | if(!items){ 1842 | return nullptr; 1843 | } 1844 | 1845 | int length = items->length; 1846 | Keys* newItems = newKeys(length + 1); 1847 | 1848 | for(int i = 0; i < index; ++i){ 1849 | (*newItems)[i] = (*items)[i]; 1850 | } 1851 | (*newItems)[index] = key; 1852 | for(int i = index; i < length; i++){ 1853 | (*newItems)[i + 1] = (*items)[i]; 1854 | } 1855 | 1856 | return newItems; 1857 | } 1858 | 1859 | template 1860 | Children* MultiwaySearchTree::generateNewChildren(Node* child, Children* children, int index){ 1861 | if(!children){ 1862 | return nullptr; 1863 | } 1864 | 1865 | int length = children->length; 1866 | Children* newItems = newChildren(length + 1); 1867 | 1868 | for(int i = 0; i < index; i++){ 1869 | (*newItems)[i] = (*children)[i]; 1870 | } 1871 | (*newItems)[index] = child; 1872 | for(int i = index; i < length; i++){ 1873 | (*newItems)[i + 1] = (*children)[i]; 1874 | } 1875 | 1876 | return newItems; 1877 | } 1878 | 1879 | template 1880 | Keys* MultiwaySearchTree::generateLeftItems(Keys* items, int index){ 1881 | if(!items){ 1882 | return nullptr; 1883 | } 1884 | 1885 | Keys* newItems = newKeys(index + 1); 1886 | 1887 | for(int i = 0; i <= index; ++i){ 1888 | (*newItems)[i] = (*items)[i]; 1889 | } 1890 | 1891 | return newItems; 1892 | } 1893 | 1894 | template 1895 | Keys* MultiwaySearchTree::generateRightItems(Keys* items, int index){ 1896 | if(!items){ 1897 | return nullptr; 1898 | } 1899 | 1900 | int length = items->length; 1901 | Keys* newItems = newKeys(length - index - 1); 1902 | 1903 | for(int i = 0, j = index + 1; j < length; ++i, ++j){ 1904 | (*newItems)[i] = (*items)[j]; 1905 | } 1906 | 1907 | return newItems; 1908 | } 1909 | template 1910 | Children* MultiwaySearchTree::generateLeftChildren(Children* children, int index){ 1911 | if(!children){ 1912 | return nullptr; 1913 | } 1914 | 1915 | Children* newItems = newChildren(index + 1); 1916 | 1917 | for(int i = 0; i <= index; ++i){ 1918 | (*newItems)[i] = (*children)[i]; 1919 | } 1920 | 1921 | return newItems; 1922 | } 1923 | 1924 | template 1925 | Children* MultiwaySearchTree::generateRightChildren(Children* children, int index){ 1926 | if(!children){ 1927 | return nullptr; 1928 | } 1929 | 1930 | int length = children->length; 1931 | Children* newItems = newChildren(length - index - 1); 1932 | 1933 | for(int i = 0, j = index + 1; j < length; ++i, ++j){ 1934 | (*newItems)[i] = (*children)[j]; 1935 | } 1936 | 1937 | return newItems; 1938 | } 1939 | 1940 | } //end of lfmst 1941 | 1942 | #endif 1943 | --------------------------------------------------------------------------------