├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── bwtree.cpp ├── bwtree.hpp ├── epoche.cpp ├── epoque.hpp ├── main.cpp ├── main.hpp └── nodes.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | build/ 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.4) 2 | project(BwTree) 3 | 4 | if( NOT CMAKE_BUILD_TYPE ) 5 | message(STATUS "No build type selected, default to Release") 6 | set( CMAKE_BUILD_TYPE Release) 7 | else() 8 | message(STATUS "Build type is set to ${CMAKE_BUILD_TYPE}") 9 | endif() 10 | 11 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -std=c++14") 12 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror -Wno-error=overflow") 13 | 14 | find_package (Threads) 15 | set(SOURCE_FILES bwtree.cpp) 16 | add_library(BwTreeLib ${SOURCE_FILES}) 17 | target_link_libraries (BwTreeLib ${CMAKE_THREAD_LIBS_INIT} tbb) 18 | 19 | add_executable(BwTree main.cpp main.hpp) 20 | target_link_libraries(BwTree BwTreeLib pthread) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014-2015 Florian Scheibner 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BwTree 2 | 3 | This demo implementation of a BwTree was developed as a student project at the Chair for database systems at the TU Munich. 4 | 5 | The BwTree is a lock free in-memory B+ Tree, the principles have been described by Microsoft Research: 6 | 7 | "The Bw-Tree: A B-tree for New Hardware Platforms": http://research.microsoft.com/pubs/178758/bw-tree-icde2013-final.pdf 8 | 9 | 10 | ## Build instructions 11 | mkdir build 12 | cd build/ 13 | cmake .. 14 | make 15 | 16 | 17 | ## Execution instructions 18 | ./BwTree 19 | 20 | By default different artificial test cases are emulated for performance measurements. 21 | The test cases can be changed in the main.cpp file. 22 | 23 | By executing with tcmalloc performance can be improved. 24 | 25 | ## Restrictions of this implementation: 26 | - Merging underful pages is not implemented. 27 | - The mapping table cannot grow dynamically 28 | - Necessary operations like consolidate or split are not executed asynchronously 29 | 30 | ## Troubleshooting 31 | - Error compiling: "error: invalid value 'c++14' in '-std=c++14'" 32 | 33 | You need a compiler that supports C++14 in order to compile the project. 34 | 35 | ## License 36 | Copyright 2014-2015 Florian Scheibner 37 | 38 | Licensed under the Apache License, Version 2.0 (the "License"); 39 | you may not use this file except in compliance with the License. 40 | You may obtain a copy of the License at 41 | 42 | http://www.apache.org/licenses/LICENSE-2.0 43 | 44 | Unless required by applicable law or agreed to in writing, software 45 | distributed under the License is distributed on an "AS IS" BASIS, 46 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 47 | See the License for the specific language governing permissions and 48 | limitations under the License. 49 | -------------------------------------------------------------------------------- /bwtree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bwtree.hpp" 5 | 6 | namespace BwTree { 7 | 8 | template 9 | FindDataPageResult::FindDataPageResult(PID const pid, Node *startNode, Node const *dataNode, PID const needConsolidatePage, PID const needSplitPage, PID const needSplitPageParent) 10 | : pid(pid), 11 | startNode(startNode), 12 | dataNode(dataNode), 13 | needConsolidatePage(needConsolidatePage), 14 | needSplitPage(needSplitPage), 15 | needSplitPageParent(needSplitPageParent) { 16 | } 17 | 18 | template 19 | FindDataPageResult::FindDataPageResult(PID const pid, Node *startNode, Node const *dataNode, Data const *data, PID const needConsolidatePage, PID const needSplitPage, PID const needSplitPageParent) 20 | : pid(pid), 21 | startNode(startNode), 22 | dataNode(dataNode), 23 | data(data), 24 | needConsolidatePage(needConsolidatePage), 25 | needSplitPage(needSplitPage), 26 | needSplitPageParent(needSplitPageParent) { 27 | } 28 | 29 | template 30 | Data *Tree::search(Key key, ThreadInfo &threadInfo) { 31 | EpocheGuard epoqueGuard(threadInfo); 32 | FindDataPageResult res = findDataPage(key); 33 | Data *returnValue; 34 | if (res.dataNode == nullptr) { 35 | returnValue = nullptr; 36 | } else { 37 | returnValue = const_cast(res.data); 38 | } 39 | if (res.needSplitPage != NotExistantPID) { 40 | splitPage(res.needSplitPage, res.needSplitPageParent); 41 | } else if (res.needConsolidatePage != NotExistantPID) { 42 | consolidatePage(res.needConsolidatePage, threadInfo); 43 | } 44 | return returnValue; 45 | } 46 | 47 | template 48 | template 49 | size_t Tree::binarySearch(T array, std::size_t length, Key key) { 50 | //std cpp code lower_bound 51 | std::size_t first = 0; 52 | std::size_t i; 53 | std::size_t count, step; 54 | count = length; 55 | while (count > 0) { 56 | i = first; 57 | step = count / 2; 58 | i += step; 59 | if (array[i].key < key) { 60 | first = ++i; 61 | count -= step + 1; 62 | } else { 63 | count = step; 64 | } 65 | } 66 | return first; 67 | } 68 | 69 | template 70 | FindDataPageResult Tree::findDataPage(Key key) { 71 | PID nextPID = root; 72 | std::size_t debugTMPCheck = 0; 73 | PID needConsolidatePage = NotExistantPID; 74 | PID needSplitPage = NotExistantPID; 75 | PID needSplitPageParent = NotExistantPID; 76 | PID parent = NotExistantPID; 77 | bool doNotSplit = false; 78 | int level = 0; 79 | while (nextPID != NotExistantPID) { 80 | if (debugTMPCheck++ > 50000) { 81 | assert(false); 82 | //std::cout << debugTMPCheck << std::endl; 83 | } 84 | std::size_t pageDepth = 0; 85 | Node *nextNode = PIDToNodePtr(nextPID); 86 | if (isLeaf(nextNode)) { 87 | break; 88 | } 89 | long deltaNodeCount = 0; 90 | long removedBySplit = 0; 91 | while (nextNode != nullptr) { 92 | ++pageDepth; 93 | assert(pageDepth < 10000); 94 | if (needConsolidatePage == NotExistantPID && ( 95 | (pageDepth == settings.getConsolidateLimitInner(level) && this->rand(this->d) - level < 40) 96 | )) {//TODO save for later 97 | needConsolidatePage = nextPID; 98 | } 99 | switch (nextNode->getType()) { 100 | case PageType::deltaIndex: { 101 | auto node1 = static_cast *>(nextNode); 102 | if (key > node1->keyLeft && key <= node1->keyRight) { 103 | level++; 104 | parent = nextPID; 105 | doNotSplit = false; 106 | nextPID = node1->child; 107 | nextNode = nullptr; 108 | continue; 109 | } else { 110 | deltaNodeCount++; 111 | nextNode = node1->origin; 112 | continue; 113 | } 114 | }; 115 | case PageType::inner: { 116 | auto node1 = static_cast *>(nextNode); 117 | if (!doNotSplit && (node1->nodeCount + deltaNodeCount - removedBySplit) > settings.getSplitLimitInner(level) && needSplitPage == NotExistantPID && this->rand(this->d) - level * 5 < 30) { 118 | if (DEBUG) std::cout << "inner count" << node1->nodeCount << " " << deltaNodeCount << " " << removedBySplit; 119 | needSplitPage = nextPID; 120 | needSplitPageParent = parent; 121 | } 122 | auto res = binarySearchnodes)>(node1->nodes, node1->nodeCount, key); 123 | if (res == node1->nodeCount) { 124 | assert(node1->next != NotExistantPID); 125 | 126 | doNotSplit = true; 127 | nextPID = node1->next; 128 | } else { 129 | level++; 130 | parent = nextPID; 131 | doNotSplit = false; 132 | nextPID = node1->nodes[res].pid; 133 | } 134 | nextNode = nullptr; 135 | continue; 136 | }; 137 | case PageType::deltaSplitInner: { 138 | auto node1 = static_cast *>(nextNode); 139 | if (key > node1->key) { 140 | nextPID = node1->sidelink; 141 | nextNode = nullptr; 142 | doNotSplit = true; 143 | continue; 144 | } 145 | removedBySplit += node1->removedElements; 146 | nextNode = node1->origin; 147 | assert(nextNode != nullptr); 148 | continue; 149 | }; 150 | default: { 151 | assert(false); // not implemented 152 | } 153 | } 154 | nextNode = nullptr; 155 | } 156 | } 157 | 158 | // Handle leaf 159 | while (nextPID != NotExistantPID) { 160 | if (debugTMPCheck++ > 50000) { 161 | assert(false); 162 | //std::cout << debugTMPCheck << std::endl; 163 | } 164 | std::size_t pageDepth = 0; 165 | Node *startNode = PIDToNodePtr(nextPID); 166 | Node *nextNode = startNode; 167 | long deltaNodeCount = 0; 168 | long removedBySplit = 0; 169 | while (nextNode != nullptr) { 170 | ++pageDepth; 171 | assert(pageDepth < 10000); 172 | if (needConsolidatePage == NotExistantPID && pageDepth == settings.getConsolidateLimitLeaf()) { 173 | //TODO save for later asynchrounous consolidate 174 | needConsolidatePage = nextPID; 175 | } 176 | switch (nextNode->getType()) { 177 | case PageType::leaf: { 178 | auto node1 = static_cast *>(nextNode); 179 | if (!doNotSplit && node1->recordCount + deltaNodeCount - removedBySplit > settings.getSplitLimitLeaf() && needSplitPage == NotExistantPID) { 180 | if (DEBUG) std::cout << "leaf count" << node1->recordCount << " " << deltaNodeCount << " " << removedBySplit; 181 | needSplitPage = nextPID; 182 | needSplitPageParent = parent; 183 | } 184 | auto res = binarySearchrecords)>(node1->records, node1->recordCount, key); 185 | if (res < node1->recordCount) { 186 | if (node1->records[res].key == key) { 187 | return FindDataPageResult(nextPID, startNode, nextNode, 188 | node1->records[res].data, 189 | needConsolidatePage, needSplitPage, 190 | needSplitPageParent); 191 | } 192 | } else if (node1->next != NotExistantPID) { 193 | doNotSplit = true; 194 | nextPID = node1->next; 195 | nextNode = nullptr; 196 | continue; 197 | } 198 | return FindDataPageResult(nextPID, startNode, nullptr, needConsolidatePage, needSplitPage, needSplitPageParent); 199 | }; 200 | case PageType::deltaInsert: { 201 | auto node1 = static_cast *>(nextNode); 202 | if (node1->record.key == key) { 203 | return FindDataPageResult(nextPID, startNode, nextNode, node1->record.data, needConsolidatePage, needSplitPage, needSplitPageParent); 204 | } 205 | deltaNodeCount++; 206 | nextNode = node1->origin; 207 | assert(nextNode != nullptr); 208 | continue; 209 | }; 210 | case PageType::deltaDelete: { 211 | auto node1 = static_cast *>(nextNode); 212 | if (node1->key == key) { 213 | return FindDataPageResult(nextPID, startNode, nullptr, needConsolidatePage, needSplitPage, needSplitPageParent); 214 | } 215 | deltaNodeCount--; 216 | nextNode = node1->origin; 217 | assert(nextNode != nullptr); 218 | continue; 219 | }; 220 | case PageType::deltaSplit: { 221 | auto node1 = static_cast *>(nextNode); 222 | if (key > node1->key) { 223 | nextPID = node1->sidelink; 224 | nextNode = startNode = nullptr; 225 | doNotSplit = true; 226 | continue; 227 | } 228 | removedBySplit += node1->removedElements; 229 | nextNode = node1->origin; 230 | assert(nextNode != nullptr); 231 | continue; 232 | }; 233 | default: { 234 | assert(false); // not implemented 235 | } 236 | } 237 | nextNode = nullptr; 238 | } 239 | } 240 | 241 | assert(false); // I think this should not happen 242 | return FindDataPageResult(NotExistantPID, nullptr, nullptr, needConsolidatePage, needSplitPage, needSplitPageParent); 243 | } 244 | 245 | template 246 | std::tuple *> Tree::findInnerNodeOnLevel(PID pid, const Key key) { 247 | PID nextPID = pid; 248 | std::size_t debugTMPCheck = 0; 249 | while (nextPID != NotExistantPID) { 250 | if (++debugTMPCheck > 0) 251 | assert(++debugTMPCheck < 100); 252 | Node *startNode = PIDToNodePtr(nextPID); 253 | Node *nextNode = startNode; 254 | while (nextNode != nullptr) { 255 | switch (nextNode->getType()) { 256 | case PageType::deltaIndex: { 257 | auto node1 = static_cast *>(nextNode); 258 | if (key > node1->keyLeft && key <= node1->keyRight) { 259 | return std::make_tuple(nextPID, startNode); 260 | } else { 261 | nextNode = node1->origin; 262 | continue; 263 | } 264 | }; 265 | case PageType::inner: { 266 | auto node1 = static_cast *>(nextNode); 267 | auto res = binarySearchnodes)>(node1->nodes, node1->nodeCount, key); 268 | if (res == node1->nodeCount && node1->next != NotExistantPID) { 269 | nextPID = node1->next; 270 | } else { 271 | return std::make_tuple(nextPID, startNode); 272 | } 273 | nextNode = nullptr; 274 | continue; 275 | }; 276 | case PageType::deltaSplitInner: { 277 | auto node1 = static_cast *>(nextNode); 278 | if (key > node1->key) { 279 | nextPID = node1->sidelink; 280 | nextNode = nullptr; 281 | continue; 282 | } 283 | nextNode = node1->origin; 284 | assert(nextNode != nullptr); 285 | continue; 286 | }; 287 | default: { 288 | assert(false); // not implemented 289 | } 290 | } 291 | nextNode = nullptr; 292 | } 293 | } 294 | assert(false); 295 | return std::make_tuple(NotExistantPID, nullptr); 296 | } 297 | 298 | template 299 | void Tree::insert(Key key, const Data *const record, ThreadInfo &threadInfo) { 300 | EpocheGuard epoqueGuard(threadInfo); 301 | restartInsert: 302 | FindDataPageResult res = findDataPage(key); 303 | assert(isLeaf(res.startNode)); 304 | if (res.needConsolidatePage == res.pid) { 305 | consolidateLeafPage(res.pid, res.startNode, threadInfo); 306 | goto restartInsert; 307 | } 308 | DeltaInsert *newNode = DeltaInsert::create(res.startNode, KeyValue(key, record), (res.dataNode != nullptr)); 309 | if (!mapping[res.pid].compare_exchange_weak(res.startNode, newNode)) { 310 | ++atomicCollisions; 311 | freeNodeSingle(newNode); 312 | goto restartInsert; 313 | } else { 314 | if (res.needSplitPage != NotExistantPID) { 315 | splitPage(res.needSplitPage, res.needSplitPageParent); 316 | } else if (res.needConsolidatePage != NotExistantPID) { 317 | consolidatePage(res.needConsolidatePage, threadInfo); 318 | } 319 | return; 320 | } 321 | } 322 | 323 | 324 | template 325 | void Tree::deleteKey(Key key, ThreadInfo &threadInfo) { 326 | EpocheGuard epoqueGuard(threadInfo); 327 | restartDelete: 328 | FindDataPageResult res = findDataPage(key); 329 | if (res.dataNode == nullptr) { 330 | return; 331 | } 332 | assert(isLeaf(res.startNode)); 333 | DeltaDelete *newDeleteNode = DeltaDelete::create(res.startNode, key); 334 | if (!mapping[res.pid].compare_exchange_weak(res.startNode, newDeleteNode)) { 335 | ++atomicCollisions; 336 | freeNodeSingle(newDeleteNode); 337 | goto restartDelete; 338 | } 339 | } 340 | 341 | template 342 | void Tree::splitPage(const PID needSplitPage, PID needSplitPageParent) { 343 | assert(needSplitPage != needSplitPageParent); 344 | if (DEBUG) std::cout << "split page" << std::endl; 345 | Node *startNode = PIDToNodePtr(needSplitPage); 346 | bool leaf = isLeaf(startNode); 347 | 348 | Key Kp, Kq; 349 | LinkedNode *newRightNode; 350 | std::size_t removedElements; 351 | if (!leaf) { 352 | static thread_local std::vector> nodesStatic; 353 | auto &nodes = nodesStatic; 354 | nodes.clear(); 355 | PID prev, next; 356 | bool hadInfinityElement; 357 | std::tie(prev, next, hadInfinityElement) = getConsolidatedInnerData(startNode, needSplitPage, nodes); 358 | 359 | if (nodes.size() < settings.getSplitLimitInner(0)) { 360 | return; 361 | } 362 | if (DEBUG) std::cout << "inner size: " << nodes.size() << std::endl; 363 | auto middle = nodes.begin(); 364 | std::advance(middle, (std::distance(nodes.begin(), nodes.end()) / 2)); 365 | std::nth_element(nodes.begin(), middle, nodes.end(), [](const KeyPid &t1, const KeyPid &t2) { 366 | return t1.key < t2.key; 367 | }); 368 | 369 | Kp = middle->key; 370 | 371 | auto newRightInner = Helper::CreateInnerNodeFromUnsorted(middle + 1, nodes.end(), needSplitPage, next, hadInfinityElement); 372 | assert(newRightInner->nodeCount > 0); 373 | Kq = newRightInner->nodes[newRightInner->nodeCount - 1].key; 374 | removedElements = newRightInner->nodeCount; 375 | newRightNode = newRightInner; 376 | } else { 377 | static thread_local std::vector> recordsStatic; 378 | auto &records = recordsStatic; 379 | records.clear(); 380 | PID prev, next; 381 | std::tie(prev, next) = getConsolidatedLeafData(startNode, records); 382 | if (DEBUG) std::cout << "leaf size: " << records.size() << std::endl; 383 | if (records.size() < settings.getSplitLimitLeaf()) { 384 | return; 385 | } 386 | auto middle = records.begin(); 387 | std::advance(middle, (std::distance(records.begin(), records.end()) / 2)); 388 | Kp = middle->key; 389 | 390 | auto newRightLeaf = Helper::CreateLeafNodeFromSorted(middle + 1, records.end(), needSplitPage, 391 | next); 392 | assert(newRightLeaf->recordCount > 0); 393 | Kq = newRightLeaf->records[newRightLeaf->recordCount - 1].key; 394 | removedElements = newRightLeaf->recordCount; 395 | newRightNode = newRightLeaf; 396 | } 397 | 398 | 399 | newRightNode->prev = needSplitPage; 400 | const PID newRightNodePID = newNode(newRightNode); 401 | DeltaSplit *splitNode; 402 | 403 | splitNode = DeltaSplit::create(startNode, Kp, newRightNodePID, removedElements, leaf); 404 | 405 | if (!mapping[needSplitPage].compare_exchange_strong(startNode, splitNode)) { 406 | ++atomicCollisions; 407 | if (!leaf) ++failedInnerSplit; else ++failedLeafSplit; 408 | freeNodeSingle(splitNode); 409 | freeNodeSingle(newRightNode); 410 | mapping[newRightNodePID].store(nullptr); 411 | return; 412 | } 413 | 414 | if (needSplitPageParent == NotExistantPID) { 415 | InnerNode *newRoot = InnerNode::create(2, NotExistantPID, NotExistantPID); 416 | newRoot->nodes[0] = KeyPid(Kp, needSplitPage); 417 | newRoot->nodes[1] = KeyPid(std::numeric_limits::max(), newRightNodePID); 418 | PID newRootPid = newNode(newRoot); 419 | PID curRoot = needSplitPage; 420 | if (root.compare_exchange_strong(curRoot, newRootPid)) { 421 | return; 422 | } 423 | freeNodeSingle(newRoot); 424 | ++atomicCollisions; 425 | needSplitPageParent = root.load(); 426 | } 427 | assert(root.load() != needSplitPage); 428 | std::size_t TMPsplitCollisions = 0; 429 | while (true) { 430 | Node *parentNode = PIDToNodePtr(needSplitPageParent); 431 | assert(!isLeaf(parentNode)); 432 | DeltaIndex *indexNode = DeltaIndex::create(parentNode, Kp, Kq, newRightNodePID, needSplitPage); 433 | if (!mapping[needSplitPageParent].compare_exchange_strong(parentNode, indexNode)) { 434 | freeNodeSingle(indexNode); 435 | ++atomicCollisions; 436 | // check if parent has been split 437 | std::tie(needSplitPageParent, parentNode) = findInnerNodeOnLevel(needSplitPageParent, Kq); 438 | if (++TMPsplitCollisions > 0) 439 | assert(TMPsplitCollisions < 100); 440 | } else { 441 | return; 442 | } 443 | } 444 | 445 | } 446 | 447 | template 448 | void Tree::consolidateLeafPage(const PID pid, Node *startNode, 449 | ThreadInfo &threadInfo) { 450 | if (DEBUG) std::cout << "consolidate leaf page" << std::endl; 451 | 452 | static thread_local std::vector> recordsStatic; 453 | auto &records = recordsStatic; 454 | records.clear(); 455 | PID prev, next; 456 | std::tie(prev, next) = getConsolidatedLeafData(startNode, records); 457 | Leaf *newNode = Helper::CreateLeafNodeFromSorted(records.begin(), records.end(), prev, 458 | next); 459 | 460 | Node *previousNode = startNode; 461 | 462 | if (!mapping[pid].compare_exchange_strong(startNode, newNode)) { 463 | freeNodeSingle(newNode); 464 | ++atomicCollisions; 465 | ++failedLeafConsolidate; 466 | } else { 467 | ++successfulLeafConsolidate; 468 | epoque.markNodeForDeletion(previousNode, threadInfo); 469 | } 470 | } 471 | 472 | template 473 | std::tuple Tree::getConsolidatedLeafData(Node *node, std::vector> &records) { 474 | std::array, 100> deltaInsertRecords; 475 | std::size_t deltaInsertRecordsCount = 0; 476 | 477 | std::array deletedOrUpdatedDeltaKeys; 478 | std::size_t deletedOrUpdatedDeltaKeysCount = 0; 479 | 480 | Key stopAtKey = std::numeric_limits::max(); 481 | bool pageSplit = false; 482 | PID prev, next; 483 | while (node->getType() != PageType::leaf) { 484 | switch (node->getType()) { 485 | case PageType::deltaInsert: { 486 | auto node1 = static_cast *>(node); 487 | auto &curKey = node1->record.key; 488 | if (curKey <= stopAtKey 489 | && std::find(deletedOrUpdatedDeltaKeys.begin(), deletedOrUpdatedDeltaKeys.begin() + 490 | deletedOrUpdatedDeltaKeysCount, curKey) == deletedOrUpdatedDeltaKeys.begin() + 491 | deletedOrUpdatedDeltaKeysCount) { 492 | deltaInsertRecords[deltaInsertRecordsCount++] = node1->record; 493 | assert(deltaInsertRecordsCount != deltaInsertRecords.size()); 494 | if (node1->keyExistedBefore) { 495 | deletedOrUpdatedDeltaKeys[deletedOrUpdatedDeltaKeysCount++] = curKey; 496 | assert(deletedOrUpdatedDeltaKeysCount != deletedOrUpdatedDeltaKeys.size()); 497 | } 498 | } 499 | node = node1->origin; 500 | continue; 501 | } 502 | case PageType::deltaDelete: { 503 | auto node1 = static_cast *>(node); 504 | auto &curKey = node1->key; 505 | if (std::find(deletedOrUpdatedDeltaKeys.begin(), deletedOrUpdatedDeltaKeys.begin() + 506 | deletedOrUpdatedDeltaKeysCount, curKey) == deletedOrUpdatedDeltaKeys.begin() + 507 | deletedOrUpdatedDeltaKeysCount) { 508 | deletedOrUpdatedDeltaKeys[deletedOrUpdatedDeltaKeysCount++] = curKey; 509 | assert(deletedOrUpdatedDeltaKeysCount != deletedOrUpdatedDeltaKeys.size()); 510 | } 511 | node = node1->origin; 512 | continue; 513 | } 514 | case PageType::deltaSplit: { 515 | auto node1 = static_cast *>(node); 516 | if (!pageSplit) { 517 | pageSplit = true; 518 | stopAtKey = node1->key; 519 | next = node1->sidelink; 520 | } 521 | node = node1->origin; 522 | continue; 523 | }; 524 | default: { 525 | assert(false); //shouldn't occur here 526 | } 527 | } 528 | } 529 | //case PageType::leaf: 530 | const auto node1 = static_cast *>(node); 531 | std::sort(deltaInsertRecords.begin(), deltaInsertRecords.begin() + deltaInsertRecordsCount, [](const KeyValue &t1, const KeyValue &t2) { 532 | return t1.key < t2.key; 533 | }); 534 | std::sort(deletedOrUpdatedDeltaKeys.begin(), deletedOrUpdatedDeltaKeys.begin() + deletedOrUpdatedDeltaKeysCount); 535 | KeyValue *recordsdata[] = {const_cast *>(deltaInsertRecords.data()), node1->records}; 536 | std::size_t nextConsideredDeltaKey = 0; 537 | std::size_t nextrecords[] = {0, 0}; 538 | std::size_t &nextdelta = nextrecords[0]; 539 | std::size_t &nextrecord = nextrecords[1]; 540 | while (nextrecord < node1->recordCount && nextdelta < deltaInsertRecordsCount) { 541 | const bool hasMoreDeltaKeys = nextConsideredDeltaKey < deletedOrUpdatedDeltaKeysCount; 542 | const bool advanceDeletedOrUpdatedDeltaKeys = 543 | hasMoreDeltaKeys && node1->records[nextrecord].key > deletedOrUpdatedDeltaKeys[nextConsideredDeltaKey]; 544 | if (advanceDeletedOrUpdatedDeltaKeys) { 545 | nextConsideredDeltaKey++; 546 | continue; 547 | } 548 | bool recordUpdatedOrDeleted = 549 | hasMoreDeltaKeys && node1->records[nextrecord].key == deletedOrUpdatedDeltaKeys[nextConsideredDeltaKey]; 550 | if (recordUpdatedOrDeleted) { 551 | nextrecord++; 552 | continue; 553 | } 554 | 555 | std::int8_t choice = (node1->records[nextrecord].key < deltaInsertRecords[nextdelta].key); 556 | KeyValue record = recordsdata[choice][nextrecords[choice]]; 557 | ++nextrecords[choice]; 558 | if (record.key <= stopAtKey) { 559 | records.push_back(record); 560 | } else { 561 | nextrecord = node1->recordCount; 562 | nextdelta = deltaInsertRecordsCount; 563 | break; 564 | } 565 | } 566 | while (nextrecord < node1->recordCount && node1->records[nextrecord].key <= stopAtKey) { 567 | if (std::find(deletedOrUpdatedDeltaKeys.begin(), 568 | deletedOrUpdatedDeltaKeys.begin() + deletedOrUpdatedDeltaKeysCount, node1->records[nextrecord].key) == 569 | deletedOrUpdatedDeltaKeys.begin() + deletedOrUpdatedDeltaKeysCount) { 570 | records.push_back(node1->records[nextrecord]); 571 | } 572 | ++nextrecord; 573 | } 574 | while (nextdelta < deltaInsertRecordsCount) { 575 | if (deltaInsertRecords[nextdelta].key <= stopAtKey) { 576 | records.push_back(deltaInsertRecords[nextdelta]); 577 | ++nextdelta; 578 | } else { 579 | break; 580 | } 581 | } 582 | prev = node1->prev; 583 | if (!pageSplit) { 584 | next = node1->next; 585 | } 586 | return std::make_tuple(prev, next); 587 | } 588 | 589 | template 590 | void Tree::consolidateInnerPage(const PID pid, Node *startNode, 591 | ThreadInfo &threadInfo) { 592 | if (DEBUG) std::cout << "consolidate inner page" << std::endl; 593 | 594 | static thread_local std::vector> nodesStatic; 595 | auto &nodes = nodesStatic; 596 | nodes.clear(); 597 | 598 | PID prev, next; 599 | bool hadInfinityElement; 600 | std::tie(prev, next, hadInfinityElement) = getConsolidatedInnerData(startNode, pid, nodes); 601 | InnerNode *newNode = Helper::CreateInnerNodeFromUnsorted(nodes.begin(), nodes.end(), prev, next, hadInfinityElement); 602 | 603 | Node *const previousNode = startNode; 604 | 605 | if (!mapping[pid].compare_exchange_strong(startNode, newNode)) { 606 | freeNodeSingle(newNode); 607 | ++atomicCollisions; 608 | ++failedInnerConsolidate; 609 | } else { 610 | ++successfulInnerConsolidate; 611 | epoque.markNodeForDeletion(previousNode, threadInfo); 612 | } 613 | } 614 | 615 | thread_local std::vector consideredPIDsConsolidateInner; 616 | 617 | template 618 | std::tuple Tree::getConsolidatedInnerData(Node *node, PID pid, std::vector> &nodes) { 619 | consideredPIDsConsolidateInner.clear(); 620 | Key stopAtKey = std::numeric_limits::max(); 621 | bool pageSplit = false; 622 | PID prev, next; 623 | bool hadInfinityElement = false; 624 | while (node != nullptr) { 625 | switch (node->getType()) { 626 | case PageType::inner: { 627 | auto node1 = static_cast *>(node); 628 | for (std::size_t i = 0; i < node1->nodeCount; ++i) { 629 | if (node1->nodes[i].key <= stopAtKey && 630 | std::find(consideredPIDsConsolidateInner.begin(), consideredPIDsConsolidateInner.end(), node1->nodes[i].pid) == consideredPIDsConsolidateInner.end()) { 631 | assert(node1->nodes[i].pid != pid); 632 | nodes.push_back(node1->nodes[i]); 633 | } 634 | } 635 | if (!pageSplit && node1->next == NotExistantPID) { 636 | hadInfinityElement = true; 637 | } 638 | prev = node1->prev; 639 | if (!pageSplit) { 640 | next = node1->next; 641 | } 642 | // found last element in the chain 643 | break; 644 | } 645 | case PageType::deltaIndex: { 646 | auto node1 = static_cast *>(node); 647 | if (node1->keyLeft <= stopAtKey && std::find(consideredPIDsConsolidateInner.begin(), consideredPIDsConsolidateInner.end(), node1->oldChild) == consideredPIDsConsolidateInner.end()) { 648 | if (node1->oldChild == pid) { 649 | assert(false); 650 | } 651 | nodes.push_back(KeyPid(node1->keyLeft, node1->oldChild)); 652 | consideredPIDsConsolidateInner.push_back(node1->oldChild); 653 | } 654 | if (node1->keyRight <= stopAtKey && std::find(consideredPIDsConsolidateInner.begin(), consideredPIDsConsolidateInner.end(), node1->child) == consideredPIDsConsolidateInner.end()) { 655 | if (node1->child == pid) { 656 | assert(false); 657 | } 658 | nodes.push_back(KeyPid(node1->keyRight, node1->child)); 659 | consideredPIDsConsolidateInner.push_back(node1->child); 660 | } 661 | node = node1->origin; 662 | continue; 663 | } 664 | case PageType::deltaSplitInner: { 665 | auto node1 = static_cast *>(node); 666 | if (!pageSplit) { 667 | pageSplit = true; 668 | stopAtKey = node1->key; 669 | next = node1->sidelink; 670 | } 671 | node = node1->origin; 672 | continue; 673 | }; 674 | default: { 675 | assert(false); //shouldn't occur here 676 | } 677 | } 678 | node = nullptr; 679 | } 680 | return std::make_tuple(prev, next, hadInfinityElement); 681 | } 682 | 683 | template 684 | Tree::~Tree() { 685 | for (unsigned long i = 0; i < mappingNext; ++i) { 686 | Node *node = mapping[i]; 687 | freeNodeRecursively(node); 688 | } 689 | } 690 | 691 | template 692 | ThreadInfo Tree::getThreadInfo() { 693 | return ThreadInfo(this->epoque); 694 | } 695 | } 696 | #include "epoche.cpp" 697 | 698 | template class BwTree::Tree; 699 | template class BwTree::Tree; 700 | template class BwTree::Tree; 701 | template class BwTree::Tree; 702 | 703 | template class BwTree::ThreadInfo; 704 | template class BwTree::ThreadInfo; 705 | template class BwTree::ThreadInfo; 706 | template class BwTree::ThreadInfo; 707 | 708 | template class BwTree::DeletionList; 709 | template class BwTree::DeletionList; 710 | template class BwTree::DeletionList; 711 | template class BwTree::DeletionList; 712 | 713 | template class BwTree::Epoche; 714 | template class BwTree::Epoche; 715 | template class BwTree::Epoche; 716 | template class BwTree::Epoche; 717 | -------------------------------------------------------------------------------- /bwtree.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BWTREE_H 2 | #define BWTREE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "nodes.hpp" 14 | #include "epoque.hpp" 15 | 16 | namespace BwTree { 17 | 18 | template 19 | struct FindDataPageResult { 20 | const PID pid; 21 | Node *startNode; 22 | const Node *const dataNode; 23 | const Data *const data = nullptr; 24 | const PID needConsolidatePage; 25 | const PID needSplitPage; 26 | const PID needSplitPageParent; 27 | 28 | 29 | FindDataPageResult(PID const pid, Node *startNode, Node const *dataNode, PID const needConsolidatePage, PID const needSplitPage, PID const needSplitPageParent); 30 | 31 | FindDataPageResult(PID const pid, Node *startNode, Node const *dataNode, Data const *data, PID const needConsolidatePage, PID const needSplitPage, PID const needSplitPageParent); 32 | }; 33 | 34 | 35 | struct Settings { 36 | std::string name; 37 | 38 | Settings(std::string name, size_t splitLeaf, std::vector const &splitInner, size_t consolidateLeaf, std::vector const &consolidateInner) 39 | : name(name), splitLeaf(splitLeaf), 40 | splitInner(splitInner), 41 | consolidateLeaf(consolidateLeaf), 42 | consolidateInner(consolidateInner) { 43 | } 44 | 45 | std::size_t splitLeaf; 46 | 47 | const std::size_t &getSplitLimitLeaf() const { 48 | return splitLeaf; 49 | } 50 | 51 | std::vector splitInner; 52 | 53 | const std::size_t &getSplitLimitInner(unsigned level) const { 54 | return level < splitInner.size() ? splitInner[level] : splitInner[splitInner.size() - 1]; 55 | } 56 | 57 | std::size_t consolidateLeaf; 58 | 59 | const std::size_t &getConsolidateLimitLeaf() const { 60 | return consolidateLeaf; 61 | } 62 | 63 | std::vector consolidateInner; 64 | 65 | const std::size_t &getConsolidateLimitInner(unsigned level) const { 66 | return level < consolidateInner.size() ? consolidateInner[level] : consolidateInner[consolidateInner.size() - 1]; 67 | } 68 | 69 | const std::string &getName() const { 70 | return name; 71 | } 72 | }; 73 | 74 | template 75 | class Tree { 76 | static constexpr bool DEBUG = false; 77 | /** 78 | * Special Invariant: 79 | * - Leaf nodes always contain special infinity value at the right end for the last pointer 80 | */ 81 | std::atomic root; 82 | std::vector *>> mapping{4194304}; 83 | std::atomic mappingNext{0}; 84 | std::atomic atomicCollisions{0}; 85 | std::atomic successfulLeafConsolidate{0}; 86 | std::atomic successfulInnerConsolidate{0}; 87 | std::atomic failedLeafConsolidate{0}; 88 | std::atomic failedInnerConsolidate{0}; 89 | std::atomic successfulLeafSplit{0}; 90 | std::atomic successfulInnerSplit{0}; 91 | std::atomic failedLeafSplit{0}; 92 | std::atomic failedInnerSplit{0}; 93 | 94 | Epoche epoque{64}; 95 | 96 | const Settings &settings; 97 | 98 | Node *PIDToNodePtr(const PID node) { 99 | return mapping[node]; 100 | } 101 | 102 | PID newNode(Node *node) { 103 | PID nextPID = mappingNext++; 104 | if (nextPID >= mapping.size()) { 105 | std::cerr << "Mapping table is full, aborting!" << std::endl; 106 | exit(1); 107 | } 108 | mapping[nextPID] = node; 109 | return nextPID; 110 | } 111 | 112 | /** 113 | * page id of the leaf node, first node in the chain (corresponds to PID), actual node where the data was found 114 | */ 115 | FindDataPageResult findDataPage(Key key); 116 | 117 | void consolidatePage(const PID pid, ThreadInfo &threadInfo) { 118 | Node *node = PIDToNodePtr(pid); 119 | if (isLeaf(node)) { 120 | consolidateLeafPage(pid, node, threadInfo); 121 | } else { 122 | consolidateInnerPage(pid, node, threadInfo); 123 | } 124 | } 125 | 126 | void consolidateInnerPage(PID pid, Node *startNode, ThreadInfo &threadInfo); 127 | 128 | std::tuple getConsolidatedInnerData(Node *node, PID pid, std::vector> &returnNodes); 129 | 130 | void consolidateLeafPage(PID pid, Node *startNode, ThreadInfo &threadInfo); 131 | 132 | std::tuple getConsolidatedLeafData(Node *node, std::vector> &returnNodes); 133 | 134 | void splitPage(const PID needSplitPage, const PID needSplitPageParent); 135 | 136 | std::tuple *> findInnerNodeOnLevel(PID pid, Key key); 137 | 138 | bool isLeaf(Node *node) { 139 | switch (node->getType()) { 140 | case PageType::inner: /* fallthrough */ 141 | case PageType::deltaSplitInner: /* fallthrough */ 142 | case PageType::deltaIndex: 143 | return false; 144 | case PageType::leaf: 145 | case PageType::deltaDelete: /* fallthrough */ 146 | case PageType::deltaSplit: /* fallthrough */ 147 | case PageType::deltaInsert: 148 | return true; 149 | } 150 | assert(false); 151 | return false; 152 | } 153 | 154 | template 155 | static size_t binarySearch(T array, std::size_t length, Key key); 156 | 157 | std::default_random_engine d; 158 | std::uniform_int_distribution rand{0, 100}; 159 | 160 | public: 161 | 162 | Tree(Settings &settings) : settings(settings) { 163 | Node *datanode = Leaf::create(0, NotExistantPID, NotExistantPID); 164 | PID dataNodePID = newNode(datanode); 165 | InnerNode *innerNode = InnerNode::create(1, NotExistantPID, NotExistantPID); 166 | innerNode->nodes[0] = KeyPid(std::numeric_limits::max(), dataNodePID); 167 | root.store(newNode(innerNode)); 168 | } 169 | 170 | ~Tree(); 171 | 172 | void insert(Key key, const Data *const record, ThreadInfo &threadInfo); 173 | 174 | void deleteKey(Key key, ThreadInfo &threadInfo); 175 | 176 | Data *search(Key key, ThreadInfo &threadInfo); 177 | 178 | ThreadInfo getThreadInfo(); 179 | 180 | /** 181 | * has to be called when no further work is to be done the next time so that the epoques can be freed. 182 | */ 183 | void threadFinishedWithTree() { 184 | //TODO EnterEpoque::threadFinishedWithTree(epoque); 185 | } 186 | 187 | 188 | unsigned long getAtomicCollisions() const { 189 | return atomicCollisions; 190 | } 191 | 192 | unsigned long getSuccessfulLeafConsolidate() const { 193 | return successfulLeafConsolidate; 194 | } 195 | 196 | unsigned long getSuccessfulInnerConsolidate() const { 197 | return successfulInnerConsolidate; 198 | } 199 | 200 | unsigned long getFailedLeafConsolidate() const { 201 | return failedLeafConsolidate; 202 | } 203 | 204 | unsigned long getFailedInnerConsolidate() const { 205 | return failedInnerConsolidate; 206 | } 207 | 208 | unsigned long getSuccessfulLeafSplit() const { 209 | return successfulLeafSplit; 210 | } 211 | 212 | unsigned long getSuccessfulInnerSplit() const { 213 | return successfulInnerSplit; 214 | } 215 | 216 | unsigned long getFailedLeafSplit() const { 217 | return failedLeafSplit; 218 | } 219 | 220 | unsigned long getFailedInnerSplit() const { 221 | return failedInnerSplit; 222 | } 223 | 224 | }; 225 | } 226 | #endif 227 | -------------------------------------------------------------------------------- /epoche.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "epoque.hpp" 4 | 5 | namespace BwTree { 6 | 7 | template 8 | DeletionList::~DeletionList() { 9 | assert(deletitionListCount == 0 && headDeletionList == nullptr); 10 | LabelDelete *cur = nullptr, *next = freeLabelDeletes; 11 | while (next != nullptr) { 12 | cur = next; 13 | next = cur->next; 14 | delete cur; 15 | } 16 | freeLabelDeletes = nullptr; 17 | } 18 | 19 | template 20 | std::size_t DeletionList::size() { 21 | return deletitionListCount; 22 | } 23 | 24 | template 25 | void DeletionList::remove(LabelDelete *label, LabelDelete *prev) { 26 | if (prev == nullptr) { 27 | headDeletionList = label->next; 28 | } else { 29 | prev->next = label->next; 30 | } 31 | deletitionListCount -= label->nodesCount; 32 | 33 | label->next = freeLabelDeletes; 34 | freeLabelDeletes = label; 35 | // TODO max of freelabeldeletes 36 | deleted += label->nodesCount; 37 | } 38 | 39 | template 40 | void DeletionList::add(Node *n) { 41 | deletitionListCount++; 42 | LabelDelete *label; 43 | if (headDeletionList != nullptr && headDeletionList->nodesCount < headDeletionList->nodes.size()) { 44 | label = headDeletionList; 45 | } else { 46 | if (freeLabelDeletes != nullptr) { 47 | label = freeLabelDeletes; 48 | freeLabelDeletes = freeLabelDeletes->next; 49 | } else { 50 | label = new LabelDelete(); 51 | } 52 | label->nodesCount = 0; 53 | label->next = headDeletionList; 54 | headDeletionList = label; 55 | } 56 | label->nodes[label->nodesCount] = n; 57 | label->nodesCount++; 58 | label->epoche = localEpoche; 59 | 60 | added++; 61 | } 62 | 63 | template 64 | LabelDelete *DeletionList::head() { 65 | return headDeletionList; 66 | } 67 | 68 | template 69 | void Epoche::enterEpoche(ThreadInfo &epocheInfo) { 70 | unsigned long curEpoche = currentEpoche.load(); 71 | if (curEpoche != epocheInfo.getDeletionList().localEpoche) { 72 | epocheInfo.getDeletionList().localEpoche.store(curEpoche); 73 | } 74 | } 75 | 76 | template 77 | void Epoche::markNodeForDeletion(Node *n, ThreadInfo &epocheInfo) { 78 | epocheInfo.getDeletionList().add(n); 79 | epocheInfo.getDeletionList().thresholdCounter++; 80 | } 81 | 82 | template 83 | void Epoche::exitEpocheAndCleanup(ThreadInfo &epocheInfo) { 84 | auto &deletionList = epocheInfo.getDeletionList(); 85 | if ((deletionList.thresholdCounter & (64 - 1)) == 0) { 86 | currentEpoche.fetch_add(1); 87 | } 88 | if (deletionList.thresholdCounter > startGCThreshhold) { 89 | if (deletionList.size() == 0) { 90 | deletionList.thresholdCounter = 1; 91 | return; 92 | } 93 | deletionList.localEpoche.store(std::numeric_limits::max()); 94 | 95 | uint64_t oldestEpoche = std::numeric_limits::max(); 96 | for (auto &epoche : deletionLists) { 97 | auto e = epoche.localEpoche.load(); 98 | if (e < oldestEpoche) { 99 | oldestEpoche = e; 100 | } 101 | } 102 | 103 | LabelDelete *cur = deletionList.head(), *next, *prev = nullptr; 104 | while (cur != nullptr) { 105 | next = cur->next; 106 | 107 | if (cur->epoche < oldestEpoche) { 108 | for (std::size_t i = 0; i < cur->nodesCount; ++i) { 109 | freeNodeRecursively(cur->nodes[i]); 110 | } 111 | deletionList.remove(cur, prev); 112 | } else { 113 | prev = cur; 114 | } 115 | cur = next; 116 | } 117 | deletionList.thresholdCounter = 1; 118 | } 119 | } 120 | 121 | template 122 | Epoche::~Epoche() { 123 | uint64_t oldestEpoche = std::numeric_limits::max(); 124 | for (auto &epoche : deletionLists) { 125 | auto e = epoche.localEpoche.load(); 126 | if (e < oldestEpoche) { 127 | oldestEpoche = e; 128 | } 129 | } 130 | for (auto &d : deletionLists) { 131 | LabelDelete *cur = d.head(), *next, *prev = nullptr; 132 | while (cur != nullptr) { 133 | next = cur->next; 134 | 135 | assert(cur->epoche < oldestEpoche); 136 | for (std::size_t i = 0; i < cur->nodesCount; ++i) { 137 | freeNodeRecursively(cur->nodes[i]); 138 | } 139 | d.remove(cur, prev); 140 | cur = next; 141 | } 142 | } 143 | } 144 | 145 | template 146 | void Epoche::showDeleteRatio() { 147 | for (auto &d : deletionLists) { 148 | std::cout << "deleted " << d.deleted << " of " << d.added << std::endl; 149 | } 150 | } 151 | 152 | template 153 | ThreadInfo::ThreadInfo(Epoche &epoche) 154 | : epoche(epoche), deletionList(epoche.deletionLists.local()) { } 155 | 156 | template 157 | DeletionList &ThreadInfo::getDeletionList() const { 158 | return deletionList; 159 | } 160 | 161 | template 162 | Epoche &ThreadInfo::getEpoche() const { 163 | return epoche; 164 | } 165 | 166 | template 167 | ThreadInfo::~ThreadInfo() { 168 | deletionList.localEpoche.store(std::numeric_limits::max()); 169 | } 170 | } -------------------------------------------------------------------------------- /epoque.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EPOQUE_HPP 2 | #define EPOQUE_HPP 3 | 4 | #include 5 | #include 6 | #include "nodes.hpp" 7 | #include "tbb/enumerable_thread_specific.h" 8 | #include "tbb/combinable.h" 9 | 10 | namespace BwTree { 11 | 12 | template 13 | struct LabelDelete { 14 | std::array*, 16> nodes; 15 | uint64_t epoche; 16 | std::size_t nodesCount; 17 | LabelDelete *next; 18 | }; 19 | 20 | template 21 | class DeletionList { 22 | LabelDelete *headDeletionList = nullptr; 23 | LabelDelete *freeLabelDeletes = nullptr; 24 | std::size_t deletitionListCount = 0; 25 | 26 | public: 27 | std::atomic localEpoche; 28 | size_t thresholdCounter{1}; 29 | 30 | ~DeletionList(); 31 | LabelDelete *head(); 32 | 33 | void add(Node *n); 34 | 35 | void remove(LabelDelete *label, LabelDelete *prev); 36 | 37 | std::size_t size(); 38 | 39 | std::uint64_t deleted = 0; 40 | std::uint64_t added = 0; 41 | }; 42 | 43 | template 44 | class Epoche; 45 | template 46 | class EpocheGuard; 47 | 48 | template 49 | class ThreadInfo { 50 | friend class Epoche; 51 | friend class EpocheGuard; 52 | Epoche &epoche; 53 | DeletionList &deletionList; 54 | 55 | Epoche &getEpoche() const; 56 | 57 | DeletionList &getDeletionList() const; 58 | public: 59 | ThreadInfo(Epoche &epoche); 60 | 61 | ~ThreadInfo(); 62 | }; 63 | 64 | template 65 | class Epoche { 66 | friend class ThreadInfo; 67 | std::atomic currentEpoche{0}; 68 | 69 | tbb::enumerable_thread_specific> deletionLists; 70 | 71 | size_t startGCThreshhold; 72 | 73 | public: 74 | Epoche(size_t startGCThreshhold) : startGCThreshhold(startGCThreshhold) { } 75 | 76 | ~Epoche(); 77 | 78 | void enterEpoche(ThreadInfo &epocheInfo); 79 | 80 | void markNodeForDeletion(Node *n, ThreadInfo &epocheInfo); 81 | 82 | void exitEpocheAndCleanup(ThreadInfo &info); 83 | 84 | void showDeleteRatio(); 85 | 86 | }; 87 | 88 | template 89 | class EpocheGuard { 90 | ThreadInfo &threadEpocheInfo; 91 | public: 92 | 93 | EpocheGuard(ThreadInfo &threadEpocheInfo) : threadEpocheInfo(threadEpocheInfo) { 94 | threadEpocheInfo.getEpoche().enterEpoche(threadEpocheInfo); 95 | } 96 | 97 | ~EpocheGuard() { 98 | threadEpocheInfo.getEpoche().exitEpocheAndCleanup(threadEpocheInfo); 99 | } 100 | }; 101 | } 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "bwtree.hpp" 8 | #include "main.hpp" 9 | 10 | using namespace BwTree; 11 | 12 | template 13 | void testBwTreeNew(const std::size_t count) { 14 | auto settings = BwTree::Settings("8,8,3,3", 8, {8}, 3, {3}); 15 | Tree tree(settings); 16 | std::vector values(count); 17 | for (std::size_t i = 0; i < count; ++i) { 18 | values.at(i) = i; 19 | tree.insert(values.at(i), &values.at(i)); 20 | } 21 | tree.deleteKey(values.at(0)); 22 | tree.deleteKey(values.at(1)); 23 | tree.deleteKey(values.at(2)); 24 | tree.deleteKey(values.at(3)); 25 | tree.deleteKey(values.at(4)); 26 | tree.deleteKey(values.at(70)); 27 | for (std::size_t i = 0; i < count; ++i) { 28 | auto *val = tree.search(values.at(i)); 29 | if (val == nullptr || *val != values.at(i)) { 30 | std::cout << "error val " << (val == nullptr ? -1 : *val) << " expected " << values.at(i) << std::endl; 31 | } 32 | } 33 | } 34 | 35 | template 36 | void testBwTree() { 37 | std::cout << "threads, operations,percent read operations, settings split leaf, settings split inner, settings delta, settings delta inner, time in ms, operations per s, exchange collisions, successful leaf consolidation, failed leaf consolidation, successful leaf split, failed leaf split," 38 | "successful inner consolidation, failed inner consolidation, successful inner split, failed innersplit" << std::endl; 39 | std::default_random_engine d; 40 | std::size_t initial_values_count = 1000000; 41 | std::uniform_int_distribution rand(1, initial_values_count * 2); 42 | std::vector initial_values(initial_values_count); 43 | { 44 | std::unordered_set keys; 45 | for (std::size_t i = 0; i < initial_values_count; ++i) { 46 | Key val; 47 | do { 48 | val = rand(d); 49 | } while (keys.find(val) != keys.end()); 50 | keys.emplace(val); 51 | initial_values[i] = val; 52 | } 53 | } 54 | std::vector numberValuesChoice{{1000000,10000000, 42000000}}; 55 | for (auto &numberValues : numberValuesChoice) { 56 | for (int numberOfThreads = 1; numberOfThreads <= 8; ++numberOfThreads) { 57 | std::uniform_int_distribution rand(1, numberValues * 2); 58 | std::vector values(numberValues); 59 | 60 | { 61 | std::unordered_set keys; 62 | for (std::size_t i = 0; i < numberValues; ++i) { 63 | unsigned long long val; 64 | do { 65 | val = rand(d); 66 | } while (keys.find(val) != keys.end()); 67 | keys.emplace(val); 68 | values[i] = val; 69 | } 70 | } 71 | 72 | 73 | std::vector settingsList{{ 74 | /* BwTree::Settings("single", 200, {100}, 5, {5}), 75 | BwTree::Settings("single", 200, {100}, 7, {6}), 76 | BwTree::Settings("single", 200, {100}, 7, {5,6}), 77 | BwTree::Settings("single", 200, {100}, 7, {4,5,6}), 78 | BwTree::Settings("single", 200, {100}, 7, {5,6,7}), 79 | 80 | BwTree::Settings("single", 200, {100}, 5, {7}),*/ 81 | /* BwTree::Settings("200, 100, 4", 200, {100}, 4, {4}), 82 | BwTree::Settings("200, 100, 5", 200, {100}, 5, {5}), 83 | BwTree::Settings("200, 100, 6", 200, {100}, 6, {6}), 84 | BwTree::Settings("200, 100, 7", 200, {100}, 7, {7}), 85 | BwTree::Settings("200, 100, 8", 200, {100}, 8, {8}), 86 | 87 | 88 | BwTree::Settings("50, 100, 7", 50, {100}, 7, {7}), 89 | BwTree::Settings("100, 100, 7", 100, {100}, 7, {7}), 90 | BwTree::Settings("200, 100, 7", 200, {100}, 7, {7}), 91 | BwTree::Settings("300, 100, 7", 300, {100}, 7, {7}), 92 | BwTree::Settings("400, 100, 7", 400, {100}, 7, {7}), 93 | 94 | BwTree::Settings("50, 200, 7", 50, {200}, 7, {7}), 95 | BwTree::Settings("100, 200, 7", 100, {200}, 7, {7}), 96 | BwTree::Settings("200, 200, 7", 200, {200}, 7, {7}), 97 | BwTree::Settings("300, 200, 7", 300, {200}, 7, {7}), 98 | BwTree::Settings("400, 200, 7", 400, {200}, 7, {7}), 99 | */ 100 | /* BwTree::Settings("400, 200, 7, 7", 400, {200}, 7, {7}), 101 | BwTree::Settings("800, 200, 7, 7", 800, {200}, 7, {7}), 102 | BwTree::Settings("8000, 200, 7, 7", 8000, {200}, 7, {7}),*/ 103 | 104 | 105 | BwTree::Settings("400, 200, 7, 7", 400, {200}, 7, {7}), 106 | BwTree::Settings("400, 400, 7, 7", 400, {400}, 7, {7}), 107 | BwTree::Settings("400, 4000, 7, 7", 400, {4000}, 7, {7}), 108 | 109 | BwTree::Settings("400, 200, 7, 27", 400, {200}, 7, {2, 7}), 110 | BwTree::Settings("400, 200, 7, 37", 400, {200}, 7, {3, 7}), 111 | BwTree::Settings("400, 200, 7, 347", 400, {200}, 7, {3, 4, 7}), 112 | 113 | BwTree::Settings("400, 200, 2, 7", 400, {200}, 7, {7}), 114 | BwTree::Settings("400, 200, 7, 7", 400, {200}, 7, {7}), 115 | BwTree::Settings("400, 200, 14, 7", 400, {200}, 7, {7}), 116 | 117 | //BwTree::Settings("single", 200, {100}, 8, {8}), 118 | 119 | 120 | 121 | //BwTree::Settings("multiple consolidate", 200, {100}, 5, {2, 3, 4}), 122 | //BwTree::Settings("multiple split and consolidate", 200, {50, 100, 200}, 5, {2, 3, 4}) 123 | }}; 124 | 125 | for (auto &settings : settingsList) { 126 | std::vector> operationsList{{std::make_tuple(values.size(), 83), std::make_tuple(values.size(), 0), std::make_tuple(values.size(), 100)}}; 127 | for (const auto &operationsTuple : operationsList) { 128 | Tree tree(settings); 129 | 130 | createBwTreeCommands(1, initial_values, initial_values, initial_values_count, 0, tree, false); 131 | const std::size_t operations = std::get<0>(operationsTuple); 132 | const std::size_t percentRead = std::get<1>(operationsTuple); 133 | auto duration = createBwTreeCommands(numberOfThreads, values, initial_values, operations, percentRead, tree, false); 134 | 135 | std::cout << numberOfThreads << "," << operations << "," << percentRead << "," << settings.getName() << ","; 136 | 137 | std::cout << duration.count() << ", "; 138 | std::cout << (duration.count() > 0 ? (operations / duration.count() * 1000) : 0) << ", "; 139 | 140 | std::cout << tree.getAtomicCollisions() << ","; 141 | std::cout << tree.getSuccessfulLeafConsolidate() << ","; 142 | std::cout << tree.getFailedLeafConsolidate() << ","; 143 | std::cout << tree.getSuccessfulLeafSplit() << ","; 144 | std::cout << tree.getFailedLeafSplit() << ","; 145 | std::cout << tree.getSuccessfulInnerConsolidate() << ","; 146 | std::cout << tree.getFailedInnerConsolidate() << ","; 147 | std::cout << tree.getSuccessfulInnerSplit() << ","; 148 | std::cout << tree.getFailedInnerSplit() << ","; 149 | std::cout << std::endl; 150 | } 151 | } 152 | }; 153 | 154 | } 155 | }; 156 | 157 | template 158 | std::chrono::milliseconds createBwTreeCommands(const std::size_t numberOfThreads, const std::vector &values, const std::vector &initial_values, const std::size_t operations, const unsigned percentRead, BwTree::Tree &tree, bool block) { 159 | std::default_random_engine d; 160 | std::uniform_int_distribution rand(1, 100); 161 | 162 | std::size_t start = 0; 163 | std::size_t delta = values.size() / numberOfThreads; 164 | std::size_t startOps = 0; 165 | std::size_t deltaOps = operations / numberOfThreads; 166 | std::vector>> commands(numberOfThreads); 167 | for (std::size_t thread_i = 0; thread_i < numberOfThreads; ++thread_i) { 168 | std::uniform_int_distribution randCoin(1, 2); 169 | std::size_t writeOperations = 0; 170 | std::vector> &cmds = commands[thread_i]; 171 | for (std::size_t op_i = 0; op_i < deltaOps; ++op_i) { 172 | if ((rand(d) < percentRead) || writeOperations == delta) { 173 | if (writeOperations != 0 && randCoin(d) == 1) { 174 | std::uniform_int_distribution randRead(0, writeOperations); 175 | cmds.push_back(BwTreeCommand(BwTreeCommandType::search, values[start + randRead(d)], nullptr)); 176 | } else { 177 | std::uniform_int_distribution randRead(0, initial_values.size()); 178 | cmds.push_back(BwTreeCommand(BwTreeCommandType::search, initial_values[randRead(d)], nullptr)); 179 | } 180 | } else { 181 | cmds.push_back(BwTreeCommand(BwTreeCommandType::insert, values[start + writeOperations], &values[start + writeOperations])); 182 | writeOperations++; 183 | } 184 | } 185 | start += delta; 186 | startOps += deltaOps; 187 | } 188 | 189 | if (block) BLOCK(); 190 | auto starttime = std::chrono::system_clock::now(); 191 | executeBwTreeCommands(commands, tree); 192 | auto duration = std::chrono::duration_cast(std::chrono::system_clock::now() - starttime); 193 | if (block) BLOCK(); 194 | 195 | return duration; 196 | } 197 | 198 | template 199 | void executeBwTreeCommands(const std::vector>> &commands, BwTree::Tree &tree) { 200 | std::vector threads; 201 | for (auto &cmds : commands) { 202 | threads.push_back(std::thread([&tree, &cmds]() { 203 | BwTree::ThreadInfo threadInfo = tree.getThreadInfo(); 204 | for (auto &command : cmds) { 205 | switch (command.type) { 206 | case BwTreeCommandType::insert: 207 | tree.insert(command.key, command.data, threadInfo); 208 | break; 209 | case BwTreeCommandType::search: 210 | tree.search(command.key, threadInfo); 211 | break; 212 | } 213 | } 214 | tree.threadFinishedWithTree(); 215 | })); 216 | } 217 | 218 | for (auto &thread : threads) { 219 | thread.join(); 220 | } 221 | } 222 | 223 | int main() { 224 | // testBwTreeNew(293); 225 | // for (std::size_t i = 20; i < 300; ++i) { 226 | // std::cout << i << std::endl; 227 | // testBwTreeNew(i); 228 | // } 229 | // return EXIT_SUCCESS; 230 | testBwTree(); 231 | return EXIT_SUCCESS; 232 | } 233 | -------------------------------------------------------------------------------- /main.hpp: -------------------------------------------------------------------------------- 1 | #define BLOCK(){std::string c;std::cout << "...";std::cin >> c;} 2 | 3 | enum class BwTreeCommandType : std::int8_t { 4 | insert, 5 | search 6 | }; 7 | 8 | template 9 | struct BwTreeCommand { 10 | const BwTreeCommandType type; 11 | const Key key; 12 | const Data *data; 13 | 14 | BwTreeCommand(BwTreeCommandType const &type, Key const key, Data const *data) : type(type), key(key), data(data) { 15 | } 16 | }; 17 | 18 | template 19 | std::chrono::milliseconds createBwTreeCommands(const std::size_t numberOfThreads, const std::vector &values, const std::vector &initial_values, const std::size_t operations, const unsigned percentRead, BwTree::Tree &tree, bool block); 20 | 21 | template 22 | void executeBwTreeCommands(const std::vector>> &commands, BwTree::Tree &tree); 23 | -------------------------------------------------------------------------------- /nodes.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NODES_HPP 2 | #define NODES_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace BwTree { 9 | using PID = std::size_t; 10 | constexpr PID NotExistantPID = std::numeric_limits::max(); 11 | 12 | enum class PageType : std::int8_t { 13 | leaf, 14 | inner, 15 | deltaInsert, 16 | deltaDelete, 17 | deltaIndex, 18 | deltaSplit, 19 | deltaSplitInner 20 | }; 21 | 22 | template 23 | struct Node { 24 | protected: 25 | PageType type; 26 | public: 27 | const PageType &getType() const { 28 | return type; 29 | } 30 | }; 31 | 32 | 33 | template 34 | struct LinkedNode : Node { 35 | PID prev; 36 | PID next; 37 | }; 38 | 39 | template 40 | struct KeyValue { 41 | Key key; 42 | const Data *data; 43 | 44 | KeyValue(const Key &key, const Data *const & data) : key(key), data(data) { } 45 | 46 | KeyValue operator=(const KeyValue &keyValue) { 47 | key = keyValue.key; 48 | data = keyValue.data; 49 | return *this; 50 | } 51 | 52 | KeyValue() { } 53 | }; 54 | 55 | template 56 | struct Leaf : LinkedNode { 57 | std::size_t recordCount; 58 | // has to be last member for dynamic operator new() !!! 59 | KeyValue records[]; 60 | 61 | static Leaf *create(std::size_t size, const PID &prev, const PID &next) { 62 | size_t s = sizeof(Leaf) + size * sizeof(std::tuple); 63 | Leaf *output = (Leaf *) operator new(s); 64 | output->recordCount = size; 65 | output->type = PageType::leaf; 66 | output->next = next; 67 | output->prev = prev; 68 | return output; 69 | } 70 | 71 | private: 72 | Leaf() = delete; 73 | 74 | ~Leaf() = delete; 75 | }; 76 | 77 | template 78 | struct KeyPid { 79 | Key key; 80 | PID pid; 81 | 82 | KeyPid(const Key key, const PID pid) : key(key), pid(pid) { } 83 | }; 84 | 85 | template 86 | struct InnerNode : LinkedNode { 87 | std::size_t nodeCount; 88 | // has to be last member for dynamic operator new() !!! 89 | KeyPid nodes[]; 90 | 91 | static InnerNode *create(std::size_t size, const PID &prev, const PID &next) { 92 | size_t s = sizeof(InnerNode) + size * sizeof(std::tuple); 93 | InnerNode *output = (InnerNode *) operator new(s); 94 | output->nodeCount = size; 95 | output->type = PageType::inner; 96 | output->next = next; 97 | output->prev = prev; 98 | return output; 99 | } 100 | 101 | private: 102 | InnerNode() = delete; 103 | 104 | ~InnerNode() = delete; 105 | }; 106 | 107 | template 108 | struct DeltaNode : Node { 109 | Node *origin; 110 | private: 111 | DeltaNode() = delete; 112 | 113 | ~DeltaNode() = delete; 114 | }; 115 | 116 | template 117 | struct DeltaInsert : DeltaNode { 118 | KeyValue record; 119 | bool keyExistedBefore; 120 | 121 | static DeltaInsert *create(Node *origin, const KeyValue record, bool keyExistedBefore) { 122 | size_t s = sizeof(DeltaInsert); 123 | DeltaInsert *output = (DeltaInsert *) operator new(s); 124 | output->type = PageType::deltaInsert; 125 | output->origin = origin; 126 | output->record = record; 127 | output->keyExistedBefore = keyExistedBefore; 128 | return output; 129 | } 130 | 131 | private: 132 | DeltaInsert() = delete; 133 | 134 | ~DeltaInsert() = delete; 135 | 136 | }; 137 | 138 | template 139 | struct DeltaDelete : DeltaNode { 140 | Key key; 141 | 142 | static DeltaDelete *create(Node *origin, Key key) { 143 | size_t s = sizeof(DeltaDelete); 144 | DeltaDelete *output = (DeltaDelete *) operator new(s); 145 | output->type = PageType::deltaDelete; 146 | output->origin = origin; 147 | output->key = key; 148 | return output; 149 | } 150 | 151 | private: 152 | DeltaDelete() = delete; 153 | 154 | ~DeltaDelete() = delete; 155 | }; 156 | 157 | template 158 | struct DeltaSplit : DeltaNode { 159 | Key key; 160 | PID sidelink; 161 | std::size_t removedElements; 162 | 163 | static DeltaSplit *create(Node *origin, Key splitKey, PID sidelink, std::size_t removedElements, bool leaf) { 164 | size_t s = sizeof(DeltaSplit); 165 | DeltaSplit *output = (DeltaSplit *) operator new(s); 166 | if (leaf) { 167 | output->type = PageType::deltaSplit; 168 | } else { 169 | output->type = PageType::deltaSplitInner; 170 | } 171 | output->origin = origin; 172 | output->key = splitKey; 173 | output->sidelink = sidelink; 174 | output->removedElements = removedElements; 175 | 176 | return output; 177 | } 178 | 179 | private: 180 | DeltaSplit() = delete; 181 | 182 | ~DeltaSplit() = delete; 183 | }; 184 | 185 | template 186 | struct DeltaIndex : DeltaNode { 187 | Key keyLeft; // greater than 188 | Key keyRight; // less or equal than 189 | PID child; 190 | PID oldChild; 191 | 192 | static DeltaIndex *create(Node *origin, Key splitKeyLeft, Key splitKeyRight, PID child, PID oldChild) { 193 | size_t s = sizeof(DeltaIndex); 194 | DeltaIndex *output = (DeltaIndex *) operator new(s); 195 | output->type = PageType::deltaIndex; 196 | output->origin = origin; 197 | output->keyLeft = splitKeyLeft; 198 | output->keyRight = splitKeyRight; 199 | output->child = child; 200 | output->oldChild = oldChild; 201 | return output; 202 | } 203 | 204 | private: 205 | DeltaIndex() = delete; 206 | 207 | ~DeltaIndex() = delete; 208 | }; 209 | 210 | 211 | template 212 | class Helper { 213 | 214 | 215 | //InnerNode *CreateInnerNodeFromUnsorted(std::vector>::const_iterator begin, std::vector>::const_iterator end, const PID &next, const PID &prev, bool infinityElement); 216 | 217 | //template 218 | public: 219 | typedef typename std::vector>::iterator InnerIterator; 220 | 221 | static InnerNode *CreateInnerNodeFromUnsorted(InnerIterator begin, InnerIterator end, const PID &prev, const PID &next, bool infinityElement) { 222 | // construct a new node 223 | auto newNode = InnerNode::create(std::distance(begin, end), prev, next); 224 | std::sort(begin, end, [](const KeyPid &t1, const KeyPid &t2) { 225 | return t1.key < t2.key; 226 | }); 227 | int i = 0; 228 | for (auto it = begin; it != end; ++it) { 229 | newNode->nodes[i++] = *it; 230 | } 231 | if (infinityElement) { 232 | newNode->nodes[newNode->nodeCount - 1].key = std::numeric_limits::max(); 233 | } 234 | return newNode; 235 | } 236 | 237 | typedef typename std::vector>::iterator LeafIterator; 238 | 239 | static Leaf *CreateLeafNodeFromSorted(LeafIterator begin, LeafIterator end, const PID &prev, 240 | const PID &next) { 241 | // construct a new node 242 | auto newNode = Leaf::create(std::distance(begin, end), prev, next); 243 | int i = 0; 244 | for (auto it = begin; it != end; ++it) { 245 | newNode->records[i++] = *it; 246 | } 247 | return newNode; 248 | } 249 | }; 250 | 251 | template 252 | void freeNodeRecursively(Node *node); 253 | 254 | template 255 | void freeNodeSingle(Node *node) { 256 | operator delete(node); 257 | } 258 | 259 | template 260 | void freeNodeRecursively(Node *node) { 261 | while (node != nullptr) { 262 | switch (node->getType()) { 263 | case PageType::inner: /* fallthrough */ 264 | case PageType::leaf: { 265 | freeNodeSingle(node); 266 | return; 267 | } 268 | case PageType::deltaIndex: /* fallthrough */ 269 | case PageType::deltaDelete: /* fallthrough */ 270 | case PageType::deltaSplitInner: /* fallthrough */ 271 | case PageType::deltaSplit: /* fallthrough */ 272 | case PageType::deltaInsert: { 273 | auto node1 = static_cast *>(node); 274 | node = node1->origin; 275 | freeNodeSingle(node1); 276 | continue; 277 | } 278 | default: { 279 | assert(false);//all nodes have to be handeled 280 | } 281 | } 282 | node = nullptr; 283 | } 284 | } 285 | } 286 | #endif --------------------------------------------------------------------------------