diff --git a/.gitignore b/.gitignore index 93885ea..b632eea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ **/*.csv build/ -.DS_Store \ No newline at end of file +.DS_Store +results/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..db62cb6 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug main.cpp", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/profile_inserts_with_file", + "args": ["-N", "50000000", "-f", "../bods/workloads/workload_N50000000_K10_L10.bin"], + "environment": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/ART.h b/ART.h index 266557f..02e8ed8 100644 --- a/ART.h +++ b/ART.h @@ -16,7 +16,6 @@ #include // gettime #include // std::random_shuffle -#include #include #include #include @@ -25,301 +24,430 @@ #include #include #include +#include +#include "Helper.h" // Helper functions #include "ArtNode.h" // ArtNode definitions #include "Chain.h" // Chain definitions -#include "Helper.h" // Helper functions namespace ART { -class ART { - public: - ArtNode* root; // pointer to root node - ArtNode* fp; // fast path - std::array fp_path; // fp path - size_t fp_path_length; // stores real length of fp path - ArtNode* fp_leaf; - - // constructor - ART() { root = NULL; } - - void insert(uint8_t key[], uintptr_t value) { - insert(this, root, &root, key, 0, value, maxPrefixLength); - } - - ArtNode* lookup(uint8_t key[]) { - return lookup(this, root, key, maxPrefixLength, 0, maxPrefixLength); - } - - Chain* rangelookup(uint8_t l_key[], unsigned l_keyLength, uint8_t h_key[], - uint8_t h_keyLength, unsigned depth, - unsigned maxKeyLength) { - return rangelookup(this, root, l_key, l_keyLength, h_key, h_keyLength, - depth, maxKeyLength); - } - - private: - // Void insert function - void insert(ART* tree, ArtNode* node, ArtNode** nodeRef, uint8_t key[], - unsigned depth, uintptr_t value, unsigned maxKeyLength) { - // Insert the leaf value into the tree - - if (node == NULL) { - *nodeRef = makeLeaf(value); - return; - } - - if (isLeaf(node)) { - // Replace leaf with Node4 and store both leaves in it - uint8_t existingKey[maxKeyLength]; - loadKey(getLeafValue(node), existingKey); - unsigned newPrefixLength = 0; - while (existingKey[depth + newPrefixLength] == - key[depth + newPrefixLength]) - newPrefixLength++; - - Node4* newNode = new Node4(); - newNode->prefixLength = newPrefixLength; - memcpy(newNode->prefix, key + depth, - min(newPrefixLength, maxPrefixLength)); - *nodeRef = newNode; - - newNode->insertNode4(this, nodeRef, - existingKey[depth + newPrefixLength], node); - newNode->insertNode4(this, nodeRef, key[depth + newPrefixLength], - makeLeaf(value)); - return; - } - - // Handle prefix of inner node - if (node->prefixLength) { - unsigned mismatchPos = - prefixMismatch(node, key, depth, maxKeyLength); - if (mismatchPos != node->prefixLength) { - // Prefix differs, create new node - Node4* newNode = new Node4(); - *nodeRef = newNode; - newNode->prefixLength = mismatchPos; - memcpy(newNode->prefix, node->prefix, - min(mismatchPos, maxPrefixLength)); - // Break up prefix - if (node->prefixLength < maxPrefixLength) { - newNode->insertNode4(this, nodeRef, - node->prefix[mismatchPos], node); - node->prefixLength -= (mismatchPos + 1); - memmove(node->prefix, node->prefix + mismatchPos + 1, - min(node->prefixLength, maxPrefixLength)); - } else { - node->prefixLength -= (mismatchPos + 1); - uint8_t minKey[maxKeyLength]; - loadKey(getLeafValue(minimum(node)), minKey); - newNode->insertNode4(this, nodeRef, - minKey[depth + mismatchPos], node); - memmove(node->prefix, minKey + depth + mismatchPos + 1, - min(node->prefixLength, maxPrefixLength)); - } - newNode->insertNode4(this, nodeRef, key[depth + mismatchPos], - makeLeaf(value)); - return; + class ART { + public: + ArtNode* root; + ArtNode* fp; + std::array fp_path; + size_t fp_path_length; + ArtNode* fp_leaf; + size_t fp_depth; + ArtNode** fp_ref; + + // constructor + ART() + : root(nullptr), + fp(nullptr), + fp_path{nullptr}, + fp_path_length(0), + fp_leaf(nullptr), + fp_depth(0), + fp_ref(nullptr) + {} + + void insert(uint8_t key[], uintptr_t value) { + insert(this, root, &root, key, 0, value, maxPrefixLength); } - depth += node->prefixLength; - } - - // Recurse - ArtNode** child = findChild(node, key[depth]); - if (*child) { - insert(tree, *child, child, key, depth + 1, value, maxKeyLength); - return; - } - - // Insert leaf into inner node - ArtNode* newNode = makeLeaf(value); - switch (node->type) { - case NodeType4: - static_cast(node)->insertNode4(this, nodeRef, - key[depth], newNode); - break; - case NodeType16: - static_cast(node)->insertNode16(this, nodeRef, - key[depth], newNode); - break; - case NodeType48: - static_cast(node)->insertNode48(this, nodeRef, - key[depth], newNode); - break; - case NodeType256: - static_cast(node)->insertNode256(this, nodeRef, - key[depth], newNode); - break; - } - } - - // Lookup function, returns ArtNode - ArtNode* lookup(ART* tree, ArtNode* node, uint8_t key[], unsigned keyLength, - unsigned depth, unsigned maxKeyLength) { - // Find the node with a matching key, optimistic version - - bool skippedPrefix = false; // Did we optimistically skip some prefix - // without checking it? - - while (node != NULL) { - if (isLeaf(node)) { - if (!skippedPrefix && depth == keyLength) // No check required - return node; - - if (depth != keyLength) { - // Check leaf - uint8_t leafKey[maxKeyLength]; - loadKey(getLeafValue(node), leafKey); - for (unsigned i = (skippedPrefix ? 0 : depth); - i < keyLength; i++) - if (leafKey[i] != key[i]) return NULL; - } - return node; + + ArtNode* lookup(uint8_t key[]) { + return lookup(this, root, key, maxPrefixLength, 0, maxPrefixLength); + } + + Chain* rangelookup(uint8_t l_key[], unsigned l_keyLength, uint8_t h_key[], + uint8_t h_keyLength, unsigned depth, unsigned maxKeyLength) { + return rangelookup(this, root, l_key, l_keyLength, h_key, h_keyLength, depth, maxKeyLength); } - if (node->prefixLength) { - if (node->prefixLength < maxPrefixLength) { - for (unsigned pos = 0; pos < node->prefixLength; pos++) - if (key[depth + pos] != node->prefix[pos]) return NULL; - } else - skippedPrefix = true; - depth += node->prefixLength; + void printTree() { + printTree(this->root, 0); } - node = *findChild(node, key[depth]); - depth++; - } - - return NULL; - } - - // Erase function, deletes a leaf from the tree - void erase(ArtNode* node, ArtNode** nodeRef, uint8_t key[], - unsigned keyLength, unsigned depth, unsigned maxKeyLength) { - // Delete a leaf from a tree - - if (!node) return; - - if (isLeaf(node)) { - // Make sure we have the right leaf - if (leafMatches(node, key, keyLength, depth, maxKeyLength)) - *nodeRef = NULL; - return; - } - - // Handle prefix - if (node->prefixLength) { - if (prefixMismatch(node, key, depth, maxKeyLength) != - node->prefixLength) - return; - depth += node->prefixLength; - } - - ArtNode** child = findChild(node, key[depth]); - if (isLeaf(*child) && - leafMatches(*child, key, keyLength, depth, maxKeyLength)) { - // Leaf found, delete it in inner node - switch (node->type) { - case NodeType4: - static_cast(node)->eraseNode4(this, nodeRef, child); - break; - case NodeType16: - static_cast(node)->eraseNode16(this, nodeRef, - child); - break; - case NodeType48: - static_cast(node)->eraseNode48(this, nodeRef, - key[depth]); - break; - case NodeType256: - static_cast(node)->eraseNode256(this, nodeRef, - key[depth]); - break; + // Method to verify the tail path after each insertion + bool verifyTailPath() { + if (this->fp_path_length == 0) { + return true; + } + + ArtNode* current = this->root; + // Traverse the tree following the fp_path + for (size_t i = 0; i < this->fp_path_length; i++) { + if (i == this->fp_path_length - 1) { + if (current == this->fp) { + if (getLeafValue(maximum(current)) == getLeafValue(this->fp_leaf)) { + return true; + } else { + printf("Error: fp_leaf mismatch. Expected %lu, got %lu.\n", + getLeafValue(maximum(current)), getLeafValue(this->fp_leaf)); + return false; + } + } else { + printf("Error: last node in fp_path is not the fp. Expected %p, got %p.\n", + static_cast(current), static_cast(this->fp)); + return false; + } + } + + // Move to the rightmost child + switch (current->type) { + case NodeType4: { + Node4* node = static_cast(current); + if (node->count > 0) { + current = node->child[node->count - 1]; + } else { + printf("Error: NodeType4 has no children.\n"); + return false; + } + break; + } + case NodeType16: { + Node16* node = static_cast(current); + if (node->count > 0) { + current = node->child[node->count - 1]; + } else { + printf("Error: NodeType16 has no children.\n"); + return false; + } + break; + } + case NodeType48: { + Node48* node = static_cast(current); + unsigned pos = 255; + while (pos > 0 && node->childIndex[pos] == emptyMarker) pos--; + if (node->childIndex[pos] != emptyMarker) { + current = node->child[node->childIndex[pos]]; + } else { + printf("Error: NodeType48 has no valid children.\n"); + return false; + } + break; + } + case NodeType256: { + Node256* node = static_cast(current); + unsigned pos = 255; + while (pos > 0 && !node->child[pos]) pos--; + if (node->child[pos]) { + current = node->child[pos]; + } else { + printf("Error: NodeType256 has no valid children.\n"); + return false; + } + break; + } + default: + printf("Error: Unknown node type.\n"); + return false; + } + } + + // If we exit the loop without returning, the path is incorrect + printf("Error: fp_path does not lead to the fp.\n"); + return false; + } + + + + private: + // Void insert function + void insert(ART* tree, ArtNode* node, ArtNode** nodeRef, uint8_t key[], unsigned depth, + uintptr_t value, unsigned maxKeyLength) { + // Insert the leaf value into the tree + + if (node == NULL) { + *nodeRef = makeLeaf(value); + return; + } + + if (isLeaf(node)) { + // Replace leaf with Node4 and store both leaves in it + uint8_t existingKey[maxKeyLength]; + loadKey(getLeafValue(node), existingKey); + unsigned newPrefixLength = 0; + while (existingKey[depth + newPrefixLength] == + key[depth + newPrefixLength]) + newPrefixLength++; + + Node4* newNode = new Node4(); + newNode->prefixLength = newPrefixLength; + memcpy(newNode->prefix, key + depth, + min(newPrefixLength, maxPrefixLength)); + *nodeRef = newNode; + + newNode->insertNode4(this, nodeRef, existingKey[depth + newPrefixLength], + node); + newNode->insertNode4(this, nodeRef, key[depth + newPrefixLength], + makeLeaf(value)); + return; + } + + // Handle prefix of inner node + if (node->prefixLength) { + unsigned mismatchPos = prefixMismatch(node, key, depth, maxKeyLength); + if (mismatchPos != node->prefixLength) { + // Prefix differs, create new node + Node4* newNode = new Node4(); + *nodeRef = newNode; + newNode->prefixLength = mismatchPos; + memcpy(newNode->prefix, node->prefix, + min(mismatchPos, maxPrefixLength)); + // Break up prefix + if (node->prefixLength < maxPrefixLength) { + newNode->insertNode4(this, nodeRef, node->prefix[mismatchPos], node); + node->prefixLength -= (mismatchPos + 1); + memmove(node->prefix, node->prefix + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } else { + node->prefixLength -= (mismatchPos + 1); + uint8_t minKey[maxKeyLength]; + loadKey(getLeafValue(minimum(node)), minKey); + newNode->insertNode4(this, nodeRef, minKey[depth + mismatchPos], + node); + memmove(node->prefix, minKey + depth + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } + newNode->insertNode4(this, nodeRef, key[depth + mismatchPos], + makeLeaf(value)); + return; + } + depth += node->prefixLength; + } + + // Recurse + ArtNode** child = findChild(node, key[depth]); + if (*child) { + insert(tree, *child, child, key, depth + 1, value, maxKeyLength); + return; + } + + // Insert leaf into inner node + ArtNode* newNode = makeLeaf(value); + switch (node->type) { + case NodeType4: + static_cast(node)->insertNode4(this, nodeRef, key[depth], + newNode); + break; + case NodeType16: + static_cast(node)->insertNode16(this, nodeRef, key[depth], + newNode); + break; + case NodeType48: + static_cast(node)->insertNode48(this, nodeRef, key[depth], + newNode); + break; + case NodeType256: + static_cast(node)->insertNode256(this, nodeRef, key[depth], + newNode); + break; + } } - } else { - // Recurse - erase(*child, child, key, keyLength, depth + 1, maxKeyLength); - } - } - - // Range lookup function, returns a Chain of ArtNode - Chain* rangelookup(ART* tree, ArtNode* node, uint8_t l_key[], - unsigned l_keyLength, uint8_t h_key[], - uint8_t h_keyLength, unsigned depth, - unsigned maxKeyLength) { - // Find the node with a matching key, optimistic version - Chain* queue = - new Chain((ChainItem*)new ChainItemWithDepth(node, 0, true, true)); - Chain* result = new Chain(); - - while (!queue->isEmpty()) { - ChainItemWithDepth* item = (ChainItemWithDepth*)queue->pop_front(); - node = item->nodeptr(); - - int depth = item->depth_; - bool lequ = item->lequ_, hequ = item->hequ_; - bool continue_flag = - 0; // true means the range vialates the key range - unsigned pos; - auto compare_and_set = [&](unsigned pos, - uint8_t compared_byte) -> void { - uint8_t lkey = pos >= l_keyLength ? 0 : l_key[pos]; - uint8_t hkey = pos >= h_keyLength ? 0 : l_key[pos]; - - if (lkey < compared_byte) - lequ = 0; - else if (lkey > compared_byte) - continue_flag = 1; - - if (hkey < compared_byte) - continue_flag = 1; - else if (hkey > compared_byte) - hequ = 0; - }; - if (isLeaf(node)) { - uint8_t leafKey[maxKeyLength]; - loadKey(getLeafValue(node), leafKey); - for (unsigned i = depth; - i < maxKeyLength && !continue_flag && (lequ || hequ); i++) - compare_and_set(i, leafKey[i]); - if (!continue_flag) { - result->extend_item(new ChainItem(node)); + + // Lookup function, returns ArtNode + ArtNode* lookup(ART* tree, ArtNode* node, uint8_t key[], unsigned keyLength, + unsigned depth, unsigned maxKeyLength) { + // Find the node with a matching key, optimistic version + + bool skippedPrefix = + false; // Did we optimistically skip some prefix without checking it? + + while (node != NULL) { + if (isLeaf(node)) { + if (!skippedPrefix && depth == keyLength) // No check required + return node; + + if (depth != keyLength) { + // Check leaf + uint8_t leafKey[maxKeyLength]; + loadKey(getLeafValue(node), leafKey); + for (unsigned i = (skippedPrefix ? 0 : depth); i < keyLength; + i++) + if (leafKey[i] != key[i]) return NULL; + } + return node; + } + + if (node->prefixLength) { + if (node->prefixLength < maxPrefixLength) { + for (unsigned pos = 0; pos < node->prefixLength; pos++) + if (key[depth + pos] != node->prefix[pos]) return NULL; + } else + skippedPrefix = true; + depth += node->prefixLength; + } + + node = *findChild(node, key[depth]); + depth++; } - continue; + + return NULL; } - if (node->prefixLength > maxPrefixLength) { - for (pos = 0; - pos < maxPrefixLength && !continue_flag && (lequ || hequ); - pos++) { - compare_and_set(depth + pos, node->prefix[pos]); + // Erase function, deletes a leaf from the tree + void erase(ArtNode* node, ArtNode** nodeRef, uint8_t key[], unsigned keyLength, + unsigned depth, unsigned maxKeyLength) { + // Delete a leaf from a tree + + if (!node) return; + + if (isLeaf(node)) { + // Make sure we have the right leaf + if (leafMatches(node, key, keyLength, depth, maxKeyLength)) + *nodeRef = NULL; + return; } - uint8_t minKey[maxKeyLength]; - loadKey(getLeafValue(minimum(node)), minKey); - for (; pos < node->prefixLength && !continue_flag && - (lequ || hequ); - pos++) { - compare_and_set(depth + pos, minKey[depth + pos]); + + // Handle prefix + if (node->prefixLength) { + if (prefixMismatch(node, key, depth, maxKeyLength) != + node->prefixLength) + return; + depth += node->prefixLength; } - } else { - for (pos = 0; pos < node->prefixLength && !continue_flag && - (lequ || hequ); - pos++) { - compare_and_set(depth + pos, node->prefix[pos]); + + ArtNode** child = findChild(node, key[depth]); + if (isLeaf(*child) && + leafMatches(*child, key, keyLength, depth, maxKeyLength)) { + // Leaf found, delete it in inner node + switch (node->type) { + case NodeType4: + static_cast(node)->eraseNode4(this, nodeRef, child); + break; + case NodeType16: + static_cast(node)->eraseNode16(this, nodeRef, child); + break; + case NodeType48: + static_cast(node)->eraseNode48(this, nodeRef, key[depth]); + break; + case NodeType256: + static_cast(node)->eraseNode256(this, nodeRef, key[depth]); + break; + } + } else { + // Recurse + erase(*child, child, key, keyLength, depth + 1, maxKeyLength); } } - if (continue_flag) continue; - depth += node->prefixLength; - - std::unique_ptr newly_added = - std::move(std::unique_ptr(newly_added->findChildbyRange( - item->nodeptr(), lequ ? l_key[depth] : 0, - hequ ? h_key[depth] : 255, depth, lequ, hequ))); - queue->extend(std::move(newly_added)); - } - delete queue; - return result; - } -}; + + // Range lookup function, returns a Chain of ArtNode + Chain* rangelookup(ART* tree, ArtNode* node, uint8_t l_key[], unsigned l_keyLength, uint8_t h_key[], uint8_t h_keyLength, + unsigned depth, unsigned maxKeyLength) { + // Find the node with a matching key, optimistic version + Chain *queue = new Chain((ChainItem *)new ChainItemWithDepth(node, 0, true, true)); + Chain *result = new Chain(); + + while (!queue->isEmpty()) { + ChainItemWithDepth *item = (ChainItemWithDepth *)queue->pop_front(); + node = item->nodeptr(); + + int depth = item->depth_; + bool lequ = item->lequ_, hequ = item->hequ_; + bool continue_flag = 0; // true means the range vialates the key range + unsigned pos; + auto compare_and_set = [&](unsigned pos, uint8_t compared_byte)->void { + uint8_t lkey = pos >= l_keyLength ? 0 : l_key[pos]; + uint8_t hkey = pos >= h_keyLength ? 0 : l_key[pos]; + + if(lkey < compared_byte) lequ = 0; + else if (lkey > compared_byte) continue_flag = 1; + + if (hkey < compared_byte) continue_flag = 1; + else if(hkey > compared_byte) hequ = 0; + }; + if(isLeaf(node)) { + uint8_t leafKey[maxKeyLength]; + loadKey(getLeafValue(node), leafKey); + for (unsigned i = depth; i < maxKeyLength && !continue_flag && (lequ || hequ); i++) + compare_and_set(i, leafKey[i]); + if(!continue_flag) { + result->extend_item(new ChainItem(node)); + } + continue; + } + + if (node->prefixLength > maxPrefixLength) { + for (pos = 0; pos < maxPrefixLength && !continue_flag && (lequ || hequ); pos++) { + compare_and_set(depth + pos, node->prefix[pos]); + } + uint8_t minKey[maxKeyLength]; + loadKey(getLeafValue(minimum(node)), minKey); + for (; pos < node->prefixLength && !continue_flag && (lequ || hequ); pos++) { + compare_and_set(depth + pos, minKey[depth + pos]); + } + } else { + for (pos = 0; pos < node->prefixLength && !continue_flag && (lequ || hequ); pos++) { + compare_and_set(depth + pos, node->prefix[pos]); + } + } + if(continue_flag) continue; + depth += node->prefixLength; + + std::unique_ptr newly_added = std::move(std::unique_ptr( + newly_added->findChildbyRange(item->nodeptr(), lequ ? l_key[depth] : 0, hequ ? h_key[depth] : 255, depth, lequ, hequ) + )); + queue->extend(std::move(newly_added)); + } + delete queue; + return result; + } + + void printTree(ArtNode* node, int depth) { + if (!node) return; + + // Indent based on depth + for (int i = 0; i < depth; i++) { + printf(" "); + } + + if (isLeaf(node)) { + printf("Leaf(%lu)\n", getLeafValue(node)); + return; + } + + switch (node->type) { + case NodeType4: { + Node4* n = static_cast(node); + printf("Node4 [%p]\n", static_cast(n)); + for (unsigned i = 0; i < n->count; i++) { + printTree(n->child[i], depth + 1); + } + break; + } + case NodeType16: { + Node16* n = static_cast(node); + printf("Node16 [%p]\n", static_cast(n)); + for (unsigned i = 0; i < n->count; i++) { + printTree(n->child[i], depth + 1); + } + break; + } + case NodeType48: { + Node48* n = static_cast(node); + printf("Node48 [%p]\n", static_cast(n)); + for (unsigned i = 0; i < 256; i++) { + if (n->childIndex[i] != emptyMarker) { + printTree(n->child[n->childIndex[i]], depth + 1); + } + } + break; + } + case NodeType256: { + Node256* n = static_cast(node); + printf("Node256 [%p]\n", static_cast(n)); + for (unsigned i = 0; i < 256; i++) { + if (n->child[i]) { + printTree(n->child[i], depth + 1); + } + } + break; + } + } + } + + }; } // namespace ART \ No newline at end of file diff --git a/ArtNode.h b/ArtNode.h index efbb491..d1366ab 100644 --- a/ArtNode.h +++ b/ArtNode.h @@ -1,481 +1,576 @@ /* - * ArtNode and its derived classes for an Adaptive Radix Tree (ART) - * implementation + * ArtNode and its derived classes for an Adaptive Radix Tree (ART) implementation */ -#pragma once - -#include -#include // x86 SSE intrinsics -#include // AVX512 -#include // integer types -#include -#include // malloc, free -#include // memset, memcpy -#include // gettime - -#include // std::random_shuffle -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Helper.h" - -namespace ART { -class ART; -// Constants for the node types -static const int8_t NodeType4 = 0; -static const int8_t NodeType16 = 1; -static const int8_t NodeType48 = 2; -static const int8_t NodeType256 = 3; - -// The maximum prefix length for compressed paths stored in the -// header, if the path is longer it is loaded from the database on -// demand -static const unsigned maxPrefixLength = 4; - -// Shared header of all inner nodes -struct ArtNode { - // length of the compressed path (prefix) - uint32_t prefixLength; - // number of non-null children - uint16_t count; - // node type - int8_t type; - // compressed path (prefix) - uint8_t prefix[maxPrefixLength]; - - ArtNode(int8_t type) : prefixLength(0), count(0), type(type) {} -}; - -// This address is used to communicate that search failed -ArtNode* nullNode = NULL; -// Empty marker -static const uint8_t emptyMarker = 48; - -// Node with up to 4 children -struct Node4 : ArtNode { - uint8_t key[4]; - ArtNode* child[4]; - - Node4() : ArtNode(NodeType4) { - memset(key, 0, sizeof(key)); - memset(child, 0, sizeof(child)); - } - - void insertNode4(ART* tree, ArtNode** nodeRef, uint8_t keyByte, - ArtNode* child); - void eraseNode4(ART* tree, ArtNode** nodeRef, ArtNode** leafPlace); -}; - -// Node with up to 16 children -struct Node16 : ArtNode { - uint8_t key[16]; - ArtNode* child[16]; - - Node16() : ArtNode(NodeType16) { - memset(key, 0, sizeof(key)); - memset(child, 0, sizeof(child)); - } - - void insertNode16(ART* tree, ArtNode** nodeRef, uint8_t keyByte, - ArtNode* child); - void eraseNode16(ART* tree, ArtNode** nodeRef, ArtNode** leafPlace); -}; - -// Node with up to 48 children -struct Node48 : ArtNode { - uint8_t childIndex[256]; - ArtNode* child[48]; - - Node48() : ArtNode(NodeType48) { - memset(childIndex, emptyMarker, sizeof(childIndex)); - memset(child, 0, sizeof(child)); - } - - void insertNode48(ART* tree, ArtNode** nodeRef, uint8_t keyByte, - ArtNode* child); - void eraseNode48(ART* tree, ArtNode** nodeRef, uint8_t keyByte); -}; - -// Node with up to 256 children -struct Node256 : ArtNode { - ArtNode* child[256]; - - Node256() : ArtNode(NodeType256) { memset(child, 0, sizeof(child)); } - - void insertNode256(ART* tree, ArtNode** nodeRef, uint8_t keyByte, - ArtNode* child); - void eraseNode256(ART* tree, ArtNode** nodeRef, uint8_t keyByte); -}; - -void copyPrefix(ArtNode* src, ArtNode* dst) { - // Helper function that copies the prefix from the source to the destination - // node - dst->prefixLength = src->prefixLength; - memcpy(dst->prefix, src->prefix, min(src->prefixLength, maxPrefixLength)); -} - -inline ArtNode* makeLeaf(uintptr_t tid) { - // Create a pseudo-leaf - return reinterpret_cast((tid << 1) | 1); -} - -inline uintptr_t getLeafValue(ArtNode* node) { - // The the value stored in the pseudo-leaf - return reinterpret_cast(node) >> 1; -} - -inline bool isLeaf(ArtNode* node) { - // Is the node a leaf? - return reinterpret_cast(node) & 1; -} - -void Node4::insertNode4(ART* tree, ArtNode** nodeRef, uint8_t keyByte, - ArtNode* child) { - // Insert leaf into inner node - if (this->count < 4) { - // Insert element - unsigned pos; - for (pos = 0; (pos < this->count) && (this->key[pos] < keyByte); pos++); - memmove(this->key + pos + 1, this->key + pos, this->count - pos); - memmove(this->child + pos + 1, this->child + pos, - (this->count - pos) * sizeof(uintptr_t)); - this->key[pos] = keyByte; - this->child[pos] = child; - this->count++; - } else { - // Grow to Node16 - Node16* newNode = new Node16(); - *nodeRef = newNode; - newNode->count = 4; - copyPrefix(this, newNode); - for (unsigned i = 0; i < 4; i++) - newNode->key[i] = flipSign(this->key[i]); - memcpy(newNode->child, this->child, this->count * sizeof(uintptr_t)); - delete this; - return newNode->insertNode16(tree, nodeRef, keyByte, child); - } -} - -void Node4::eraseNode4(ART* tree, ArtNode** nodeRef, ArtNode** leafPlace) { - // Delete leaf from inner node - unsigned pos = leafPlace - this->child; - memmove(this->key + pos, this->key + pos + 1, this->count - pos - 1); - memmove(this->child + pos, this->child + pos + 1, - (this->count - pos - 1) * sizeof(uintptr_t)); - this->count--; - - if (this->count == 1) { - // Get rid of one-way node - ArtNode* child = this->child[0]; - if (!isLeaf(child)) { - // Concantenate prefixes - unsigned l1 = this->prefixLength; - if (l1 < maxPrefixLength) { - this->prefix[l1] = this->key[0]; - l1++; - } - if (l1 < maxPrefixLength) { - unsigned l2 = min(child->prefixLength, maxPrefixLength - l1); - memcpy(this->prefix + l1, child->prefix, l2); - l1 += l2; + #pragma once + + #include + #include // x86 SSE intrinsics + #include // AVX512 + + #include // integer types + #include + #include // malloc, free + #include // memset, memcpy + #include // gettime + + #include // std::random_shuffle + #include + + #include "Helper.h" + + #include + #include + #include + #include + #include + #include + #include + + #include + + namespace ART { + class ART; + // Constants for the node types + static const int8_t NodeType4 = 0; + static const int8_t NodeType16 = 1; + static const int8_t NodeType48 = 2; + static const int8_t NodeType256 = 3; + + // The maximum prefix length for compressed paths stored in the + // header, if the path is longer it is loaded from the database on + // demand + static const unsigned maxPrefixLength = 4; + + // Shared header of all inner nodes + struct ArtNode { + // length of the compressed path (prefix) + uint32_t prefixLength; + // number of non-null children + uint16_t count; + // node type + int8_t type; + // compressed path (prefix) + uint8_t prefix[maxPrefixLength]; + + ArtNode(int8_t type) : prefixLength(0), count(0), type(type) {} + }; + + // This address is used to communicate that search failed + ArtNode* nullNode = NULL; + // Empty marker + static const uint8_t emptyMarker = 48; + + // Node with up to 4 children + struct Node4 : ArtNode { + uint8_t key[4]; + ArtNode* child[4]; + + Node4() : ArtNode(NodeType4) { + memset(key, 0, sizeof(key)); + memset(child, 0, sizeof(child)); + } + + // Base ART insert function for Node4 + void insertNode4(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child); + // Insert function used in base tail insert. Checks if fp structures need + // to be updated and updates if necessary + void tailInsertNode4(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, std::array& temp_fp_path, + size_t& temp_fp_path_length, size_t depth_prev); + // Insert function used in xtail insert when key is less than the leaf value + // Only updates the existing fp structures if needed. + void insertNode4OnlyUpdateFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child); + // Insert function used in xtail insert when key is greater than the leaf value + // and always in lil insert. Always updates the existing fp structures. + void insertNode4AlwaysChangeFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, size_t depth_prev); + // Erase function for Node4 + void eraseNode4(ART* tree, ArtNode** nodeRef, ArtNode** leafPlace); + + }; + + // Node with up to 16 children + struct Node16 : ArtNode { + uint8_t key[16]; + ArtNode* child[16]; + + Node16() : ArtNode(NodeType16) { + memset(key, 0, sizeof(key)); + memset(child, 0, sizeof(child)); + } + // Base ART insert function for Node16 + void insertNode16(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child); + // Insert function used in base tail insert. Checks if fp structures need + // to be updated and updates if necessary. + void tailInsertNode16(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, std::array& temp_fp_path, + size_t& temp_fp_path_length, size_t depth_prev); + // Insert function used in xtail insert when key is less than the leaf value + // Only updates the existing fp structures if needed. + void insertNode16OnlyUpdateFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child); + // Insert function used in xtail insert when key is greater than the leaf value + // and always in lil insert. Always updates the existing fp structures. + void insertNode16AlwaysChangeFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, size_t depth_prev); + // Erase function for Node16 + void eraseNode16(ART* tree, ArtNode** nodeRef, ArtNode** leafPlace); + + }; + + // Node with up to 48 children + struct Node48 : ArtNode { + uint8_t childIndex[256]; + ArtNode* child[48]; + + Node48() : ArtNode(NodeType48) { + memset(childIndex, emptyMarker, sizeof(childIndex)); + memset(child, 0, sizeof(child)); + } + + // Base ART insert function for Node48 + void insertNode48(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child); + // Insert function used in base tail insert. Checks if fp structures need + // to be updated and updates if necessary. + void tailInsertNode48(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, std::array& temp_fp_path, + size_t& temp_fp_path_length, size_t depth_prev); + // Insert function used in xtail insert when key is less than the leaf value + // Only updates the existing fp structures if needed. + void insertNode48OnlyUpdateFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child); + // Insert function used in xtail insert when key is greater than the leaf value + // and always in lil insert. Always updates the existing fp structures. + void insertNode48AlwaysChangeFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, size_t depth_prev); + // Erase function for Node48 + void eraseNode48(ART* tree, ArtNode** nodeRef, uint8_t keyByte); + + }; + + // Node with up to 256 children + struct Node256 : ArtNode { + ArtNode* child[256]; + + Node256() : ArtNode(NodeType256) { memset(child, 0, sizeof(child)); } + + // Base ART insert function for Node256 + void insertNode256(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child); + // Insert function used in base tail insert. Checks if fp structures need + // to be updated and updates if necessary. + void tailInsertNode256(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, std::array& temp_fp_path, + size_t& temp_fp_path_length, size_t depth_prev); + // Insert function used in xtail insert when key is less than the leaf value + // Only updates the existing fp structures if needed. + void insertNode256OnlyUpdateFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child); + // Insert function used in xtail insert when key is greater than the leaf value + // and always in lil insert. Always updates the existing fp structures. + void insertNode256AlwaysChangeFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, size_t depth_prev); + // Erase function for Node256 + void eraseNode256(ART* tree, ArtNode** nodeRef, uint8_t keyByte); + + }; + + void copyPrefix(ArtNode* src, ArtNode* dst) { + // Helper function that copies the prefix from the source to the destination + // node + dst->prefixLength = src->prefixLength; + memcpy(dst->prefix, src->prefix, min(src->prefixLength, maxPrefixLength)); + } + + inline ArtNode* makeLeaf(uintptr_t tid) { + // Create a pseudo-leaf + return reinterpret_cast((tid << 1) | 1); + } + + inline uintptr_t getLeafValue(ArtNode* node) { + // The the value stored in the pseudo-leaf + return reinterpret_cast(node) >> 1; + } + + inline bool isLeaf(ArtNode* node) { + // Is the node a leaf? + return reinterpret_cast(node) & 1; + } + + void Node4::insertNode4(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child) { + // Insert leaf into inner node + if (this->count < 4) { + // Insert element + unsigned pos; + for (pos = 0; (pos < this->count) && (this->key[pos] < keyByte); pos++); + memmove(this->key + pos + 1, this->key + pos, this->count - pos); + memmove(this->child + pos + 1, this->child + pos, + (this->count - pos) * sizeof(uintptr_t)); + this->key[pos] = keyByte; + this->child[pos] = child; + this->count++; + } else { + // Grow to Node16 + Node16* newNode = new Node16(); + *nodeRef = newNode; + newNode->count = 4; + copyPrefix(this, newNode); + for (unsigned i = 0; i < 4; i++) + newNode->key[i] = flipSign(this->key[i]); + memcpy(newNode->child, this->child, this->count * sizeof(uintptr_t)); + delete this; + return newNode->insertNode16(tree, nodeRef, keyByte, child); + } + } + + void Node4::eraseNode4(ART* tree, ArtNode** nodeRef, ArtNode** leafPlace) { + // Delete leaf from inner node + unsigned pos = leafPlace - this->child; + memmove(this->key + pos, this->key + pos + 1, this->count - pos - 1); + memmove(this->child + pos, this->child + pos + 1, + (this->count - pos - 1) * sizeof(uintptr_t)); + this->count--; + + if (this->count == 1) { + // Get rid of one-way node + ArtNode* child = this->child[0]; + if (!isLeaf(child)) { + // Concantenate prefixes + unsigned l1 = this->prefixLength; + if (l1 < maxPrefixLength) { + this->prefix[l1] = this->key[0]; + l1++; + } + if (l1 < maxPrefixLength) { + unsigned l2 = min(child->prefixLength, maxPrefixLength - l1); + memcpy(this->prefix + l1, child->prefix, l2); + l1 += l2; + } + // Store concantenated prefix + memcpy(child->prefix, this->prefix, min(l1, maxPrefixLength)); + child->prefixLength += this->prefixLength + 1; + } + *nodeRef = child; + delete this; + } + } + + void Node16::insertNode16(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child) { + // Insert leaf into inner node + if (this->count < 16) { + // Insert element + uint8_t keyByteFlipped = flipSign(keyByte); + __m128i cmp = _mm_cmplt_epi8( + _mm_set1_epi8(keyByteFlipped), + _mm_loadu_si128(reinterpret_cast<__m128i*>(this->key))); + uint16_t bitfield = + _mm_movemask_epi8(cmp) & (0xFFFF >> (16 - this->count)); + unsigned pos = bitfield ? ctz(bitfield) : this->count; + memmove(this->key + pos + 1, this->key + pos, this->count - pos); + memmove(this->child + pos + 1, this->child + pos, + (this->count - pos) * sizeof(uintptr_t)); + this->key[pos] = keyByteFlipped; + this->child[pos] = child; + this->count++; + } else { + // Grow to Node48 + Node48* newNode = new Node48(); + *nodeRef = newNode; + memcpy(newNode->child, this->child, this->count * sizeof(uintptr_t)); + for (unsigned i = 0; i < this->count; i++) + newNode->childIndex[flipSign(this->key[i])] = i; + copyPrefix(this, newNode); + newNode->count = this->count; + delete this; + return newNode->insertNode48(tree, nodeRef, keyByte, child); + } + } + + void Node16::eraseNode16(ART* tree, ArtNode** nodeRef, ArtNode** leafPlace) { + // Delete leaf from inner node + unsigned pos = leafPlace - this->child; + memmove(this->key + pos, this->key + pos + 1, this->count - pos - 1); + memmove(this->child + pos, this->child + pos + 1, + (this->count - pos - 1) * sizeof(uintptr_t)); + this->count--; + + if (this->count == 3) { + // Shrink to Node4 + Node4* newNode = new Node4(); + newNode->count = this->count; + copyPrefix(this, newNode); + for (unsigned i = 0; i < 4; i++) + newNode->key[i] = flipSign(this->key[i]); + memcpy(newNode->child, this->child, sizeof(uintptr_t) * 4); + *nodeRef = newNode; + delete this; + } + } + + void Node48::insertNode48(ART* tree, ArtNode** nodeRef, uint8_t keyByte, ArtNode* child) { + // Insert leaf into inner node + if (this->count < 48) { + // Insert element + unsigned pos = this->count; + if (this->child[pos]) + for (pos = 0; this->child[pos] != NULL; pos++); + this->child[pos] = child; + this->childIndex[keyByte] = pos; + this->count++; + } else { + // Grow to Node256 + Node256* newNode = new Node256(); + for (unsigned i = 0; i < 256; i++) + if (this->childIndex[i] != 48) + newNode->child[i] = this->child[this->childIndex[i]]; + newNode->count = this->count; + copyPrefix(this, newNode); + *nodeRef = newNode; + delete this; + return newNode->insertNode256(tree, nodeRef, keyByte, child); + } + } + + void Node48::eraseNode48(ART* tree, ArtNode** nodeRef, uint8_t keyByte) { + // Delete leaf from inner node + this->child[this->childIndex[keyByte]] = NULL; + this->childIndex[keyByte] = emptyMarker; + this->count--; + + if (this->count == 12) { + // Shrink to Node16 + Node16* newNode = new Node16(); + *nodeRef = newNode; + copyPrefix(this, newNode); + for (unsigned b = 0; b < 256; b++) { + if (this->childIndex[b] != emptyMarker) { + newNode->key[newNode->count] = flipSign(b); + newNode->child[newNode->count] = + this->child[this->childIndex[b]]; + newNode->count++; + } + } + delete this; + } + } + + void Node256::insertNode256(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child) { + // Insert leaf into inner node + this->count++; + this->child[keyByte] = child; + } + + + void Node256::eraseNode256(ART* tree, ArtNode** nodeRef, uint8_t keyByte) { + // Delete leaf from inner node + this->child[keyByte] = NULL; + this->count--; + + if (this->count == 37) { + // Shrink to Node48 + Node48* newNode = new Node48(); + *nodeRef = newNode; + copyPrefix(this, newNode); + for (unsigned b = 0; b < 256; b++) { + if (this->child[b]) { + newNode->childIndex[b] = newNode->count; + newNode->child[newNode->count] = this->child[b]; + newNode->count++; + } + } + delete this; + } + } + + + ArtNode** findChild(ArtNode* n, uint8_t keyByte) { + // Find the next child for the keyByte + switch (n->type) { + case NodeType4: { + Node4* node = static_cast(n); + for (unsigned i = 0; i < node->count; i++) + if (node->key[i] == keyByte) return &node->child[i]; + return &nullNode; + } + case NodeType16: { + Node16* node = static_cast(n); + __m128i cmp = _mm_cmpeq_epi8( + _mm_set1_epi8(flipSign(keyByte)), + _mm_loadu_si128(reinterpret_cast<__m128i*>(node->key))); + unsigned bitfield = + _mm_movemask_epi8(cmp) & ((1 << node->count) - 1); + if (bitfield) + return &node->child[ctz(bitfield)]; + else + return &nullNode; + } + case NodeType48: { + Node48* node = static_cast(n); + if (node->childIndex[keyByte] != emptyMarker) + return &node->child[node->childIndex[keyByte]]; + else + return &nullNode; + } + case NodeType256: { + Node256* node = static_cast(n); + return &(node->child[keyByte]); + } + } + throw; // Unreachable + } + + ArtNode* minimum(ArtNode* node) { + // Find the leaf with smallest key + if (!node) return NULL; + + if (isLeaf(node)) return node; + + switch (node->type) { + case NodeType4: { + Node4* n = static_cast(node); + return minimum(n->child[0]); + } + case NodeType16: { + Node16* n = static_cast(node); + return minimum(n->child[0]); + } + case NodeType48: { + Node48* n = static_cast(node); + unsigned pos = 0; + while (n->childIndex[pos] == emptyMarker) pos++; + return minimum(n->child[n->childIndex[pos]]); + } + case NodeType256: { + Node256* n = static_cast(node); + unsigned pos = 0; + while (!n->child[pos]) pos++; + return minimum(n->child[pos]); + } + } + throw; // Unreachable + } + + ArtNode* maximum(ArtNode* node) { + // Find the leaf with largest key + if (!node) return NULL; + + if (isLeaf(node)) return node; + + switch (node->type) { + case NodeType4: { + Node4* n = static_cast(node); + return maximum(n->child[n->count - 1]); + } + case NodeType16: { + Node16* n = static_cast(node); + return maximum(n->child[n->count - 1]); + } + case NodeType48: { + Node48* n = static_cast(node); + unsigned pos = 255; + while (n->childIndex[pos] == emptyMarker) pos--; + return maximum(n->child[n->childIndex[pos]]); + } + case NodeType256: { + Node256* n = static_cast(node); + unsigned pos = 255; + while (!n->child[pos]) pos--; + return maximum(n->child[pos]); + } + } + throw; // Unreachable + } + + bool leafMatches(ArtNode* leaf, uint8_t key[], unsigned keyLength, + unsigned depth, unsigned maxKeyLength) { + // Check if the key of the leaf is equal to the searched key + if (depth != keyLength) { + uint8_t leafKey[maxKeyLength]; + loadKey(getLeafValue(leaf), leafKey); + for (unsigned i = depth; i < keyLength; i++) + if (leafKey[i] != key[i]) return false; + } + return true; + } + + unsigned prefixMismatch(ArtNode* node, uint8_t key[], unsigned depth, + unsigned maxKeyLength) { + // Compare the key with the prefix of the node, return the number matching + // bytes + unsigned pos; + if (node->prefixLength > maxPrefixLength) { + for (pos = 0; pos < maxPrefixLength; pos++) + if (key[depth + pos] != node->prefix[pos]) return pos; + uint8_t minKey[maxKeyLength]; + loadKey(getLeafValue(minimum(node)), minKey); + for (; pos < node->prefixLength; pos++) + if (key[depth + pos] != minKey[depth + pos]) return pos; + } else { + for (pos = 0; pos < node->prefixLength; pos++) + if (key[depth + pos] != node->prefix[pos]) return pos; + } + return pos; + } + + ArtNode* lookupPessimistic(ArtNode* node, uint8_t key[], unsigned keyLength, + unsigned depth, unsigned maxKeyLength) { + // Find the node with a matching key, alternative pessimistic version + + while (node != NULL) { + if (isLeaf(node)) { + if (leafMatches(node, key, keyLength, depth, maxKeyLength)) + return node; + return NULL; + } + + if (prefixMismatch(node, key, depth, maxKeyLength) != + node->prefixLength) + return NULL; + else + depth += node->prefixLength; + + node = *findChild(node, key[depth]); + depth++; + } + + return NULL; + } + + void printFpPath(std::array path, size_t path_length) { + // Print the fp path for debugging + for (size_t i = 0; i < path_length; i++) { + if (isLeaf(path[i])) { + printf("Leaf(%lu)\n", getLeafValue(path[i])); + } + else { + switch (path[i]->type) { + case NodeType4: + printf("Node4 %p\n", path[i]); + break; + case NodeType16: + printf("Node16 %p\n", path[i]); + break; + case NodeType48: + printf("Node48 %p\n", path[i]); + break; + case NodeType256: + printf("Node256 %p\n", path[i]); + break; + default: + printf("Unknown NodeType %p\n", path[i]); + break; + } } - // Store concantenated prefix - memcpy(child->prefix, this->prefix, min(l1, maxPrefixLength)); - child->prefixLength += this->prefixLength + 1; } - *nodeRef = child; - delete this; } -} - -void Node16::insertNode16(ART* tree, ArtNode** nodeRef, uint8_t keyByte, - ArtNode* child) { - // Insert leaf into inner node - if (this->count < 16) { - // Insert element - uint8_t keyByteFlipped = flipSign(keyByte); - __m128i cmp = _mm_cmplt_epi8( - _mm_set1_epi8(keyByteFlipped), - _mm_loadu_si128(reinterpret_cast<__m128i*>(this->key))); - uint16_t bitfield = - _mm_movemask_epi8(cmp) & (0xFFFF >> (16 - this->count)); - unsigned pos = bitfield ? ctz(bitfield) : this->count; - memmove(this->key + pos + 1, this->key + pos, this->count - pos); - memmove(this->child + pos + 1, this->child + pos, - (this->count - pos) * sizeof(uintptr_t)); - this->key[pos] = keyByteFlipped; - this->child[pos] = child; - this->count++; - } else { - // Grow to Node48 - Node48* newNode = new Node48(); - *nodeRef = newNode; - memcpy(newNode->child, this->child, this->count * sizeof(uintptr_t)); - for (unsigned i = 0; i < this->count; i++) - newNode->childIndex[flipSign(this->key[i])] = i; - copyPrefix(this, newNode); - newNode->count = this->count; - delete this; - return newNode->insertNode48(tree, nodeRef, keyByte, child); - } -} - -void Node16::eraseNode16(ART* tree, ArtNode** nodeRef, ArtNode** leafPlace) { - // Delete leaf from inner node - unsigned pos = leafPlace - this->child; - memmove(this->key + pos, this->key + pos + 1, this->count - pos - 1); - memmove(this->child + pos, this->child + pos + 1, - (this->count - pos - 1) * sizeof(uintptr_t)); - this->count--; - - if (this->count == 3) { - // Shrink to Node4 - Node4* newNode = new Node4(); - newNode->count = this->count; - copyPrefix(this, newNode); - for (unsigned i = 0; i < 4; i++) - newNode->key[i] = flipSign(this->key[i]); - memcpy(newNode->child, this->child, sizeof(uintptr_t) * 4); - *nodeRef = newNode; - delete this; - } -} - -void Node48::insertNode48(ART* tree, ArtNode** nodeRef, uint8_t keyByte, - ArtNode* child) { - // Insert leaf into inner node - if (this->count < 48) { - // Insert element - unsigned pos = this->count; - if (this->child[pos]) - for (pos = 0; this->child[pos] != NULL; pos++); - this->child[pos] = child; - this->childIndex[keyByte] = pos; - this->count++; - } else { - // Grow to Node256 - Node256* newNode = new Node256(); - for (unsigned i = 0; i < 256; i++) - if (this->childIndex[i] != 48) - newNode->child[i] = this->child[this->childIndex[i]]; - newNode->count = this->count; - copyPrefix(this, newNode); - *nodeRef = newNode; - delete this; - return newNode->insertNode256(tree, nodeRef, keyByte, child); - } -} - -void Node48::eraseNode48(ART* tree, ArtNode** nodeRef, uint8_t keyByte) { - // Delete leaf from inner node - this->child[this->childIndex[keyByte]] = NULL; - this->childIndex[keyByte] = emptyMarker; - this->count--; - - if (this->count == 12) { - // Shrink to Node16 - Node16* newNode = new Node16(); - *nodeRef = newNode; - copyPrefix(this, newNode); - for (unsigned b = 0; b < 256; b++) { - if (this->childIndex[b] != emptyMarker) { - newNode->key[newNode->count] = flipSign(b); - newNode->child[newNode->count] = - this->child[this->childIndex[b]]; - newNode->count++; - } - } - delete this; - } -} - -void Node256::insertNode256(ART* tree, ArtNode** nodeRef, uint8_t keyByte, - ArtNode* child) { - // Insert leaf into inner node - this->count++; - this->child[keyByte] = child; -} - -void Node256::eraseNode256(ART* tree, ArtNode** nodeRef, uint8_t keyByte) { - // Delete leaf from inner node - this->child[keyByte] = NULL; - this->count--; - - if (this->count == 37) { - // Shrink to Node48 - Node48* newNode = new Node48(); - *nodeRef = newNode; - copyPrefix(this, newNode); - for (unsigned b = 0; b < 256; b++) { - if (this->child[b]) { - newNode->childIndex[b] = newNode->count; - newNode->child[newNode->count] = this->child[b]; - newNode->count++; - } - } - delete this; - } -} - -ArtNode** findChild(ArtNode* n, uint8_t keyByte) { - // Find the next child for the keyByte - switch (n->type) { - case NodeType4: { - Node4* node = static_cast(n); - for (unsigned i = 0; i < node->count; i++) - if (node->key[i] == keyByte) return &node->child[i]; - return &nullNode; - } - case NodeType16: { - Node16* node = static_cast(n); - __m128i cmp = _mm_cmpeq_epi8( - _mm_set1_epi8(flipSign(keyByte)), - _mm_loadu_si128(reinterpret_cast<__m128i*>(node->key))); - unsigned bitfield = - _mm_movemask_epi8(cmp) & ((1 << node->count) - 1); - if (bitfield) - return &node->child[ctz(bitfield)]; - else - return &nullNode; - } - case NodeType48: { - Node48* node = static_cast(n); - if (node->childIndex[keyByte] != emptyMarker) - return &node->child[node->childIndex[keyByte]]; - else - return &nullNode; - } - case NodeType256: { - Node256* node = static_cast(n); - return &(node->child[keyByte]); - } - } - throw; // Unreachable -} - -ArtNode* minimum(ArtNode* node) { - // Find the leaf with smallest key - if (!node) return NULL; - - if (isLeaf(node)) return node; - - switch (node->type) { - case NodeType4: { - Node4* n = static_cast(node); - return minimum(n->child[0]); - } - case NodeType16: { - Node16* n = static_cast(node); - return minimum(n->child[0]); - } - case NodeType48: { - Node48* n = static_cast(node); - unsigned pos = 0; - while (n->childIndex[pos] == emptyMarker) pos++; - return minimum(n->child[n->childIndex[pos]]); - } - case NodeType256: { - Node256* n = static_cast(node); - unsigned pos = 0; - while (!n->child[pos]) pos++; - return minimum(n->child[pos]); - } - } - throw; // Unreachable -} - -ArtNode* maximum(ArtNode* node) { - // Find the leaf with largest key - if (!node) return NULL; - - if (isLeaf(node)) return node; - - switch (node->type) { - case NodeType4: { - Node4* n = static_cast(node); - return maximum(n->child[n->count - 1]); - } - case NodeType16: { - Node16* n = static_cast(node); - return maximum(n->child[n->count - 1]); - } - case NodeType48: { - Node48* n = static_cast(node); - unsigned pos = 255; - while (n->childIndex[pos] == emptyMarker) pos--; - return maximum(n->child[n->childIndex[pos]]); - } - case NodeType256: { - Node256* n = static_cast(node); - unsigned pos = 255; - while (!n->child[pos]) pos--; - return maximum(n->child[pos]); - } - } - throw; // Unreachable -} - -bool leafMatches(ArtNode* leaf, uint8_t key[], unsigned keyLength, - unsigned depth, unsigned maxKeyLength) { - // Check if the key of the leaf is equal to the searched key - if (depth != keyLength) { - uint8_t leafKey[maxKeyLength]; - loadKey(getLeafValue(leaf), leafKey); - for (unsigned i = depth; i < keyLength; i++) - if (leafKey[i] != key[i]) return false; - } - return true; -} - -unsigned prefixMismatch(ArtNode* node, uint8_t key[], unsigned depth, - unsigned maxKeyLength) { - // Compare the key with the prefix of the node, return the number matching - // bytes - unsigned pos; - if (node->prefixLength > maxPrefixLength) { - for (pos = 0; pos < maxPrefixLength; pos++) - if (key[depth + pos] != node->prefix[pos]) return pos; - uint8_t minKey[maxKeyLength]; - loadKey(getLeafValue(minimum(node)), minKey); - for (; pos < node->prefixLength; pos++) - if (key[depth + pos] != minKey[depth + pos]) return pos; - } else { - for (pos = 0; pos < node->prefixLength; pos++) - if (key[depth + pos] != node->prefix[pos]) return pos; - } - return pos; -} - -ArtNode* lookupPessimistic(ArtNode* node, uint8_t key[], unsigned keyLength, - unsigned depth, unsigned maxKeyLength) { - // Find the node with a matching key, alternative pessimistic version - - while (node != NULL) { - if (isLeaf(node)) { - if (leafMatches(node, key, keyLength, depth, maxKeyLength)) - return node; - return NULL; - } - - if (prefixMismatch(node, key, depth, maxKeyLength) != - node->prefixLength) - return NULL; - else - depth += node->prefixLength; - - node = *findChild(node, key[depth]); - depth++; - } - - return NULL; -} -} // namespace ART \ No newline at end of file + + } \ No newline at end of file diff --git a/ArtNodeNewMethods.cpp b/ArtNodeNewMethods.cpp new file mode 100644 index 0000000..71982f3 --- /dev/null +++ b/ArtNodeNewMethods.cpp @@ -0,0 +1,479 @@ +#include "ArtNode.h" +#include "ART.h" + +/* + * QuART related insertNodeX methods + */ + +namespace ART { + // fp insert method for Node4 + void Node4::tailInsertNode4(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, std::array& temp_fp_path, + size_t& temp_fp_path_length, size_t depth_prev) { + // Insert leaf into inner node + if (this->count < 4) { + // Insert element + unsigned pos; + for (pos = 0; (pos < this->count) && (this->key[pos] < keyByte); pos++); + memmove(this->key + pos + 1, this->key + pos, this->count - pos); + memmove(this->child + pos + 1, this->child + pos, + (this->count - pos) * sizeof(uintptr_t)); + this->key[pos] = keyByte; + this->child[pos] = child; + + // If what's being inserted is a leaf + if (isLeaf(child)) { + // If the new value is greater than or equal to the current fp_leaf, + // update the fp_leaf, fp and fp_path + if (getLeafValue(child) >= getLeafValue(tree->fp_leaf)) { + tree->fp_leaf = child; + tree->fp = temp_fp_path[temp_fp_path_length - 1]; + tree->fp_path = temp_fp_path; + tree->fp_path_length = temp_fp_path_length; // update fp_path size + tree->fp_depth = depth_prev; + tree->fp_ref = nodeRef; + } + } + + this->count++; + } else { + // Grow to Node16 + Node16* newNode = new Node16(); + *nodeRef = newNode; + newNode->count = 4; + copyPrefix(this, newNode); + for (unsigned i = 0; i < 4; i++) + newNode->key[i] = flipSign(this->key[i]); + memcpy(newNode->child, this->child, this->count * sizeof(uintptr_t)); + + // The sizes of temp_fp_path and fp_path before operations + int temp_fp_path_length_old = temp_fp_path_length; + int fp_path_length_old = tree->fp_path_length; + // If the changing node is on the fp_path + if (temp_fp_path_length_old <= fp_path_length_old && tree->fp_path[temp_fp_path_length_old-1] == this) { + // Change the node to the newNode which has a greater capacity + temp_fp_path[temp_fp_path_length_old - 1] = newNode; + // If the new value doesn't create a new fp_leaf, restore the remaining part of the fp_path + if (getLeafValue(child) < getLeafValue(tree->fp_leaf)) { + // create a deep copy of remainder of fp_path here + std::array fp_path_remainder; + std::copy(tree->fp_path.begin() + temp_fp_path_length_old, tree->fp_path.end(), fp_path_remainder.begin()); + tree->fp_path = temp_fp_path; // update fp_path + tree->fp_path_length = temp_fp_path_length_old; // update fp_path size + // Add the remaining part of the fp_path + for (int i = 0; i < fp_path_length_old - temp_fp_path_length_old; i++) { + tree->fp_path[i + temp_fp_path_length_old] = fp_path_remainder[i]; + tree->fp_path_length++; + } + tree->fp = tree->fp_path[tree->fp_path_length - 1]; + } + } + + delete this; + return newNode->tailInsertNode16(tree, nodeRef, keyByte, child, temp_fp_path, temp_fp_path_length, depth_prev); + } + } + + // fp insert method for Node16 + void Node16::tailInsertNode16(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, std::array& temp_fp_path, + size_t& temp_fp_path_length, size_t depth_prev) { + // Insert leaf into inner node + if (this->count < 16) { + // Insert element + uint8_t keyByteFlipped = flipSign(keyByte); + __m128i cmp = _mm_cmplt_epi8( + _mm_set1_epi8(keyByteFlipped), + _mm_loadu_si128(reinterpret_cast<__m128i*>(this->key))); + uint16_t bitfield = + _mm_movemask_epi8(cmp) & (0xFFFF >> (16 - this->count)); + unsigned pos = bitfield ? ctz(bitfield) : this->count; + memmove(this->key + pos + 1, this->key + pos, this->count - pos); + memmove(this->child + pos + 1, this->child + pos, + (this->count - pos) * sizeof(uintptr_t)); + + this->key[pos] = keyByteFlipped; + this->child[pos] = child; + + // If what's being inserted is a leaf + if (isLeaf(child)) { + // If the new value is greater than or equal to the current fp_leaf, + // update the fp_leaf, fp and fp_path + if (getLeafValue(child) >= getLeafValue(tree->fp_leaf)) { + tree->fp_leaf = child; + tree->fp = temp_fp_path[temp_fp_path_length - 1]; + tree->fp_path = temp_fp_path; + tree->fp_path_length = temp_fp_path_length; + tree->fp_depth = depth_prev; + tree->fp_ref = nodeRef; + } + } + + this->count++; + } else { + // Grow to Node48 + Node48* newNode = new Node48(); + *nodeRef = newNode; + memcpy(newNode->child, this->child, this->count * sizeof(uintptr_t)); + for (unsigned i = 0; i < this->count; i++) + newNode->childIndex[flipSign(this->key[i])] = i; + copyPrefix(this, newNode); + newNode->count = this->count; + + // The sizes of temp_fp_path and fp_path before operations + int temp_fp_path_length_old = temp_fp_path_length; + int fp_path_length_old = tree->fp_path_length; + // If the changing node is on the fp_path + if (temp_fp_path_length_old <= fp_path_length_old && tree->fp_path[temp_fp_path_length_old-1] == this) { + // Change the node to the newNode which has a greater capacity + temp_fp_path[temp_fp_path_length_old - 1] = newNode; + // If the new value doesn't create a new fp_leaf, restore the remaining part of the fp_path + if (getLeafValue(child) < getLeafValue(tree->fp_leaf)) { + // create a deep copy of remainder of fp_path here + std::array fp_path_remainder; + std::copy(tree->fp_path.begin() + temp_fp_path_length_old, tree->fp_path.end(), fp_path_remainder.begin()); + tree->fp_path = temp_fp_path; // update fp_path + tree->fp_path_length = temp_fp_path_length_old; // update fp_path size + // Add the remaining part of the fp_path + for (int i = 0; i < fp_path_length_old - temp_fp_path_length_old; i++) { + tree->fp_path[i + temp_fp_path_length_old] = fp_path_remainder[i]; + tree->fp_path_length++; + } + tree->fp = tree->fp_path[tree->fp_path_length - 1]; // update the fp pointer + } + } + + delete this; + return newNode->tailInsertNode48(tree, nodeRef, keyByte, child, temp_fp_path, temp_fp_path_length, depth_prev); + } + } + + // fp insert method for Node48 + void Node48::tailInsertNode48(ART* tree, ArtNode** nodeRef, uint8_t keyByte, ArtNode* child, + std::array& temp_fp_path, size_t& temp_fp_path_length, size_t depth_prev) { + // Insert leaf into inner node + if (this->count < 48) { + // Insert element + unsigned pos = this->count; + if (this->child[pos]) + for (pos = 0; this->child[pos] != NULL; pos++); + this->child[pos] = child; + this->childIndex[keyByte] = pos; + this->count++; + + // If what's being inserted is a leaf + if (isLeaf(child)) { + // If the new value is greater than or equal to the current fp_leaf, + // update the fp_leaf, fp and fp_path + if (getLeafValue(child) >= getLeafValue(tree->fp_leaf)) { + tree->fp_leaf = child; + tree->fp = temp_fp_path[temp_fp_path_length - 1]; + tree->fp_path = temp_fp_path; + tree->fp_path_length = temp_fp_path_length; + tree->fp_depth = depth_prev; + tree->fp_ref = nodeRef; + } + } + + } else { + // Grow to Node256 + Node256* newNode = new Node256(); + for (unsigned i = 0; i < 256; i++) + if (this->childIndex[i] != 48) + newNode->child[i] = this->child[this->childIndex[i]]; + newNode->count = this->count; + copyPrefix(this, newNode); + *nodeRef = newNode; + + // The sizes of temp_fp_path and fp_path before operations + int temp_fp_path_length_old = temp_fp_path_length; + int fp_path_length_old = tree->fp_path_length; + // If the changing node is on the fp_path + if (temp_fp_path_length_old <= fp_path_length_old && tree->fp_path[temp_fp_path_length_old-1] == this) { + // Change the node to the newNode which has a greater capacity + temp_fp_path[temp_fp_path_length_old - 1] = newNode; + // If the new value doesn't create a new fp_leaf, restore the remaining part of the fp_path + if (getLeafValue(child) < getLeafValue(tree->fp_leaf)) { + // create a deep copy of remainder of fp_path here + std::array fp_path_remainder; + std::copy(tree->fp_path.begin() + temp_fp_path_length_old, tree->fp_path.end(), fp_path_remainder.begin()); + tree->fp_path = temp_fp_path; // update fp_path + tree->fp_path_length = temp_fp_path_length_old; // update fp_path size + // Add the remaining part of the fp_path + for (int i = 0; i < fp_path_length_old - temp_fp_path_length_old; i++) { + tree->fp_path[i + temp_fp_path_length_old] = fp_path_remainder[i]; + tree->fp_path_length++; + } + tree->fp = tree->fp_path[tree->fp_path_length - 1]; // update the fp pointer + } + } + + delete this; + return newNode->tailInsertNode256(tree, nodeRef, keyByte, child, temp_fp_path, temp_fp_path_length, depth_prev); + } + } + + // fp insert method for Node256 + void Node256::tailInsertNode256(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, std::array& temp_fp_path, + size_t& temp_fp_path_length, size_t depth_prev) { + // Insert leaf into inner node + this->count++; + this->child[keyByte] = child; + + // If what's being inserted is a leaf + if (isLeaf(child)) { + // If the new value is greater than or equal to the current fp_leaf, + // update the fp_leaf, fp and fp_path + if (getLeafValue(child) >= getLeafValue(tree->fp_leaf)) { + tree->fp_leaf = child; + tree->fp = temp_fp_path[temp_fp_path_length - 1]; + tree->fp_path = temp_fp_path; + tree->fp_path_length = temp_fp_path_length; + tree->fp_depth = depth_prev; + tree->fp_ref = nodeRef; + } + } + + } + + + void Node4::insertNode4OnlyUpdateFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child) { + // Insert leaf into inner node + if (this->count < 4) { + // Insert element + unsigned pos; + for (pos = 0; (pos < this->count) && (this->key[pos] < keyByte); pos++); + memmove(this->key + pos + 1, this->key + pos, this->count - pos); + memmove(this->child + pos + 1, this->child + pos, + (this->count - pos) * sizeof(uintptr_t)); + this->key[pos] = keyByte; + this->child[pos] = child; + this->count++; + } else { + // Grow to Node16 + Node16* newNode = new Node16(); + *nodeRef = newNode; + + // If the changing node is on the fp_path + if (tree->fp_path[tree->fp_path_length - 1] == this) { + tree->fp_path[tree->fp_path_length - 1] = newNode; + tree->fp = newNode; + } + + newNode->count = 4; + copyPrefix(this, newNode); + for (unsigned i = 0; i < 4; i++) + newNode->key[i] = flipSign(this->key[i]); + memcpy(newNode->child, this->child, this->count * sizeof(uintptr_t)); + delete this; + return newNode->insertNode16OnlyUpdateFp(tree, nodeRef, keyByte, child); + } + } + + void Node16::insertNode16OnlyUpdateFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child) { + // Insert leaf into inner node + if (this->count < 16) { + // Insert element + uint8_t keyByteFlipped = flipSign(keyByte); + __m128i cmp = _mm_cmplt_epi8( + _mm_set1_epi8(keyByteFlipped), + _mm_loadu_si128(reinterpret_cast<__m128i*>(this->key))); + uint16_t bitfield = + _mm_movemask_epi8(cmp) & (0xFFFF >> (16 - this->count)); + unsigned pos = bitfield ? ctz(bitfield) : this->count; + memmove(this->key + pos + 1, this->key + pos, this->count - pos); + memmove(this->child + pos + 1, this->child + pos, + (this->count - pos) * sizeof(uintptr_t)); + this->key[pos] = keyByteFlipped; + this->child[pos] = child; + this->count++; + } else { + // Grow to Node48 + Node48* newNode = new Node48(); + *nodeRef = newNode; + + // If the changing node is on the fp_path + if (tree->fp_path[tree->fp_path_length - 1] == this) { + tree->fp_path[tree->fp_path_length - 1] = newNode; + tree->fp = newNode; + } + + memcpy(newNode->child, this->child, this->count * sizeof(uintptr_t)); + for (unsigned i = 0; i < this->count; i++) + newNode->childIndex[flipSign(this->key[i])] = i; + copyPrefix(this, newNode); + newNode->count = this->count; + delete this; + return newNode->insertNode48OnlyUpdateFp(tree, nodeRef, keyByte, child); + } + } + + void Node48::insertNode48OnlyUpdateFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, ArtNode* child) { + // Insert leaf into inner node + if (this->count < 48) { + // Insert element + unsigned pos = this->count; + if (this->child[pos]) + for (pos = 0; this->child[pos] != NULL; pos++); + this->child[pos] = child; + this->childIndex[keyByte] = pos; + this->count++; + } else { + // Grow to Node256 + Node256* newNode = new Node256(); + for (unsigned i = 0; i < 256; i++) + if (this->childIndex[i] != 48) + newNode->child[i] = this->child[this->childIndex[i]]; + newNode->count = this->count; + copyPrefix(this, newNode); + *nodeRef = newNode; + + // If the changing node is on the fp_path + if (tree->fp_path[tree->fp_path_length - 1] == this) { + tree->fp_path[tree->fp_path_length - 1] = newNode; + tree->fp = newNode; + } + delete this; + return newNode->insertNode256OnlyUpdateFp(tree, nodeRef, keyByte, child); + } + } + + void Node256::insertNode256OnlyUpdateFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child) { + // Insert leaf into inner node + this->count++; + this->child[keyByte] = child; + } + + // fp insert method for Node4 + void Node4::insertNode4AlwaysChangeFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, size_t depth_prev) { + // Insert leaf into inner node + if (this->count < 4) { + // Insert element + unsigned pos; + for (pos = 0; (pos < this->count) && (this->key[pos] < keyByte); pos++); + memmove(this->key + pos + 1, this->key + pos, this->count - pos); + memmove(this->child + pos + 1, this->child + pos, + (this->count - pos) * sizeof(uintptr_t)); + this->key[pos] = keyByte; + this->child[pos] = child; + + tree->fp_leaf = child; + tree->fp = tree->fp_path[tree->fp_path_length - 1]; + tree->fp_depth = depth_prev; + tree->fp_ref = nodeRef; + + this->count++; + } else { + // Grow to Node16 + Node16* newNode = new Node16(); + *nodeRef = newNode; + newNode->count = 4; + copyPrefix(this, newNode); + for (unsigned i = 0; i < 4; i++) + newNode->key[i] = flipSign(this->key[i]); + memcpy(newNode->child, this->child, this->count * sizeof(uintptr_t)); + + tree->fp_path[tree->fp_path_length - 1] = newNode; + + delete this; + return newNode->insertNode16AlwaysChangeFp(tree, nodeRef, keyByte, child, depth_prev); + } + } + + // fp insert method for Node16 + void Node16::insertNode16AlwaysChangeFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, size_t depth_prev) { + // Insert leaf into inner node + if (this->count < 16) { + // Insert element + uint8_t keyByteFlipped = flipSign(keyByte); + __m128i cmp = _mm_cmplt_epi8( + _mm_set1_epi8(keyByteFlipped), + _mm_loadu_si128(reinterpret_cast<__m128i*>(this->key))); + uint16_t bitfield = + _mm_movemask_epi8(cmp) & (0xFFFF >> (16 - this->count)); + unsigned pos = bitfield ? ctz(bitfield) : this->count; + memmove(this->key + pos + 1, this->key + pos, this->count - pos); + memmove(this->child + pos + 1, this->child + pos, + (this->count - pos) * sizeof(uintptr_t)); + + this->key[pos] = keyByteFlipped; + this->child[pos] = child; + + tree->fp_leaf = child; + tree->fp = tree->fp_path[tree->fp_path_length - 1]; + tree->fp_depth = depth_prev; + tree->fp_ref = nodeRef; + + this->count++; + } else { + // Grow to Node48 + Node48* newNode = new Node48(); + *nodeRef = newNode; + memcpy(newNode->child, this->child, this->count * sizeof(uintptr_t)); + for (unsigned i = 0; i < this->count; i++) + newNode->childIndex[flipSign(this->key[i])] = i; + copyPrefix(this, newNode); + newNode->count = this->count; + + tree->fp_path[tree->fp_path_length - 1] = newNode; + + delete this; + return newNode->insertNode48AlwaysChangeFp(tree, nodeRef, keyByte, child, depth_prev); + } + } + + // fp insert method for Node48 + void Node48::insertNode48AlwaysChangeFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, ArtNode* child, size_t depth_prev) { + // Insert leaf into inner node + if (this->count < 48) { + // Insert element + unsigned pos = this->count; + if (this->child[pos]) + for (pos = 0; this->child[pos] != NULL; pos++); + this->child[pos] = child; + this->childIndex[keyByte] = pos; + this->count++; + + tree->fp_leaf = child; + tree->fp = tree->fp_path[tree->fp_path_length - 1]; + tree->fp_depth = depth_prev; + tree->fp_ref = nodeRef; + + } else { + // Grow to Node256 + Node256* newNode = new Node256(); + for (unsigned i = 0; i < 256; i++) + if (this->childIndex[i] != 48) + newNode->child[i] = this->child[this->childIndex[i]]; + newNode->count = this->count; + copyPrefix(this, newNode); + *nodeRef = newNode; + + tree->fp_path[tree->fp_path_length - 1] = newNode; + + delete this; + return newNode->insertNode256AlwaysChangeFp(tree, nodeRef, keyByte, child, depth_prev); + } + } + + // fp insert method for Node256 + void Node256::insertNode256AlwaysChangeFp(ART* tree, ArtNode** nodeRef, uint8_t keyByte, + ArtNode* child, size_t depth_prev) { + // Insert leaf into inner node + this->count++; + this->child[keyByte] = child; + + tree->fp_leaf = child; + tree->fp = tree->fp_path[tree->fp_path_length - 1]; + tree->fp_depth = depth_prev; + tree->fp_ref = nodeRef; + + } + +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d3ced37..433c4bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,9 +11,16 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build (Debug or Release)" FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release") +include_directories(${CMAKE_SOURCE_DIR}) + # Set compiler flags for different build types -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2") set(CMAKE_CXX_FLAGS_DEBUG "-g -O0") set(CMAKE_CXX_FLAGS_RELEASE "-O3") -add_executable(main main.cpp) -add_executable(test_range test_range_query.cpp) +add_executable(test_range benchmarks/test_range_query.cpp) +add_executable(profile_inserts benchmarks/profile_inserts.cpp) +add_executable(profile_inserts_with_file benchmarks/profile_inserts_with_file.cpp) +add_executable(art benchmarks/art.cpp) +add_executable(quart_tail benchmarks/quart_tail.cpp) +add_executable(quart_xtail benchmarks/quart_xtail.cpp) +add_executable(quart_lil benchmarks/quart_lil.cpp) \ No newline at end of file diff --git a/QuARTVariants/QuART_lil.h b/QuARTVariants/QuART_lil.h new file mode 100644 index 0000000..80287f1 --- /dev/null +++ b/QuARTVariants/QuART_lil.h @@ -0,0 +1,276 @@ +#pragma once + +#include "ART.h" +#include "ArtNode.h" + +namespace ART { + + class QuART_lil : public ART { + public: + + QuART_lil() : ART() {} + + void insert(uint8_t key[], uintptr_t value) { + + // Check if we can lil insert + ArtNode* root = this->root; + // Check if the root is not null and is not a leaf + if (root != nullptr && !isLeaf(root)) { + int leafValue = getLeafValue(this->fp_leaf); + // For each byte in the key excluding the last byte, + // check if it matches the corresponding byte in the leaf value + // If any byte does not match, set can_lil_insert to false + for (size_t i = 0; i < maxPrefixLength - 1; ++i) { + uint8_t leafByte = (leafValue >> (8 * (maxPrefixLength - 1 - i))) & 0xFF; + if (leafByte != key[i]) { + // If the key defers from leafByte earlier, we lil insert from root + this->fp_path = {this->root}; + this->fp_path_length = 1; + QuART_lil::insert_recursive_always_change_fp(this,this->root, &this->root, key, 0, value, maxPrefixLength); + return; + } + } + } + else { + // If the root is null or is a leaf, we cannot lil insert + fp_path = {this->root}; + fp_path_length = 1; + QuART_lil::insert_recursive_always_change_fp(this, this->root, &this->root, key, 0, value, maxPrefixLength); + return; + } + + // We reached the last byte of the key, we can lil insert + //printf("doing lil insert for value: %lu, value on leaf node was: %lu\n", value, getLeafValue(this->fp_leaf)); + QuART_lil::insert_recursive_only_update_fp(this, this->fp, this->fp_ref, + key, fp_depth, value, maxPrefixLength); + return; + } + + private: + + void insert_recursive_only_update_fp(ART* tree, ArtNode* node, ArtNode** nodeRef, uint8_t key[], unsigned depth, + uintptr_t value, unsigned maxKeyLength) { + + // Insert the leaf value into the tree + if (node == NULL) { + *nodeRef = makeLeaf(value); + // Adjust only fp_leaf (fp will still be null) + tree->fp_leaf = *nodeRef; + tree->fp_ref = nodeRef; + return; + } + + if (isLeaf(node)) { + + // Replace leaf with Node4 and store both leaves in it + uint8_t existingKey[maxKeyLength]; + loadKey(getLeafValue(node), existingKey); + unsigned newPrefixLength = 0; + while (existingKey[depth + newPrefixLength] == + key[depth + newPrefixLength]) + newPrefixLength++; + + Node4* newNode = new Node4(); + newNode->prefixLength = newPrefixLength; + memcpy(newNode->prefix, key + depth, + min(newPrefixLength, maxPrefixLength)); + *nodeRef = newNode; + + // If the changing node was the fp just straight change the node + if (tree->fp_leaf == node) { + this->fp_path[this->fp_path_length] = newNode; + this->fp_path_length++; + this->fp = newNode; + this->fp_ref = nodeRef; + this->fp_depth = depth; + } + newNode->insertNode4(this, nodeRef, existingKey[depth + newPrefixLength], + node); + newNode->insertNode4(this, nodeRef, key[depth + newPrefixLength], + makeLeaf(value)); + return; + } + + // Handle prefix of inner node + if (node->prefixLength) { + unsigned mismatchPos = prefixMismatch(node, key, depth, maxKeyLength); + if (mismatchPos != node->prefixLength) { + // Prefix differs, create new node + Node4* newNode = new Node4(); + *nodeRef = newNode; + newNode->prefixLength = mismatchPos; + memcpy(newNode->prefix, node->prefix, + min(mismatchPos, maxPrefixLength)); + // Break up prefix + if (node->prefixLength < maxPrefixLength) { + // If the nodes that being changed is in fp_path + auto it = std::find(fp_path.begin(), fp_path.begin() + fp_path_length, node); + if (it != fp_path.begin() + fp_path_length) { + // Find the position of node in fp_path + size_t pos = std::distance(fp_path.begin(), it); + std::copy_backward(fp_path.begin() + pos, fp_path.begin() + fp_path_length, fp_path.begin() + fp_path_length + 1); + fp_path[pos] = newNode; + fp_path_length++; + } + newNode->insertNode4(this, nodeRef, node->prefix[mismatchPos], node); + node->prefixLength -= (mismatchPos + 1); + memmove(node->prefix, node->prefix + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } else { + node->prefixLength -= (mismatchPos + 1); + uint8_t minKey[maxKeyLength]; + loadKey(getLeafValue(minimum(node)), minKey); + // If the nodes that being changed is in fp_path + auto it = std::find(fp_path.begin(), fp_path.begin() + fp_path_length, node); + if (it != fp_path.begin() + fp_path_length) { + // Find the position of node in fp_path + size_t pos = std::distance(fp_path.begin(), it); + std::copy_backward(fp_path.begin() + pos, fp_path.begin() + fp_path_length, fp_path.begin() + fp_path_length + 1); + fp_path[pos] = newNode; + fp_path_length++; + } + newNode->insertNode4(this, nodeRef, minKey[depth + mismatchPos], + node); + memmove(node->prefix, minKey + depth + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } + newNode->insertNode4(this, nodeRef, key[depth + mismatchPos], + makeLeaf(value)); + return; + } + depth += node->prefixLength; + } + + // Recurse + ArtNode** child = findChild(node, key[depth]); + if (*child) { + insert_recursive_only_update_fp(tree, *child, child, key, depth + 1, value, maxKeyLength); + return; + } + + // Insert leaf into inner node + ArtNode* newNode = makeLeaf(value); + switch (node->type) { + case NodeType4: + static_cast(node)->insertNode4OnlyUpdateFp(this, nodeRef, key[depth], newNode); + break; + case NodeType16: + static_cast(node)->insertNode16OnlyUpdateFp(this, nodeRef, key[depth], newNode); + break; + case NodeType48: + static_cast(node)->insertNode48OnlyUpdateFp(this, nodeRef, key[depth], newNode); + break; + case NodeType256: + static_cast(node)->insertNode256OnlyUpdateFp(this, nodeRef, key[depth], newNode); + break; + } + } + + + void insert_recursive_always_change_fp(ART* tree, ArtNode* node, ArtNode** nodeRef, uint8_t key[], unsigned depth, + uintptr_t value, unsigned maxKeyLength) { + + size_t depth_prev = depth; + + // Insert the leaf value into the tree + if (node == NULL) { + *nodeRef = makeLeaf(value); + // Adjust only fp_leaf (fp will still be null) + tree->fp_leaf = *nodeRef; + tree->fp_ref = nodeRef; + return; + } + + if (isLeaf(node)) { + // Replace leaf with Node4 and store both leaves in it + uint8_t existingKey[maxKeyLength]; + loadKey(getLeafValue(node), existingKey); + unsigned newPrefixLength = 0; + while (existingKey[depth + newPrefixLength] == + key[depth + newPrefixLength]) + newPrefixLength++; + + Node4* newNode = new Node4(); + newNode->prefixLength = newPrefixLength; + memcpy(newNode->prefix, key + depth, + min(newPrefixLength, maxPrefixLength)); + *nodeRef = newNode; + + fp_path[fp_path_length - 1] = newNode; + + newNode->insertNode4(this, nodeRef, existingKey[depth + newPrefixLength], + node); + newNode->insertNode4AlwaysChangeFp(this, nodeRef, key[depth + newPrefixLength], + makeLeaf(value), depth_prev); + return; + } + + // Handle prefix of inner node + if (node->prefixLength) { + unsigned mismatchPos = prefixMismatch(node, key, depth, maxKeyLength); + if (mismatchPos != node->prefixLength) { + // Prefix differs, create new node + Node4* newNode = new Node4(); + *nodeRef = newNode; + newNode->prefixLength = mismatchPos; + memcpy(newNode->prefix, node->prefix, + min(mismatchPos, maxPrefixLength)); + // Break up prefix + if (node->prefixLength < maxPrefixLength) { + // In all cases, newNode should be added to fp_path + fp_path[fp_path_length - 1] = newNode; + // If the nodes that being changed is in fp_path + newNode->insertNode4(this, nodeRef, node->prefix[mismatchPos], node); + node->prefixLength -= (mismatchPos + 1); + memmove(node->prefix, node->prefix + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } else { + node->prefixLength -= (mismatchPos + 1); + uint8_t minKey[maxKeyLength]; + loadKey(getLeafValue(minimum(node)), minKey); + // In all cases, newNode should be added to fp_path + fp_path[fp_path_length - 1] = newNode; + newNode->insertNode4(this, nodeRef, minKey[depth + mismatchPos], node); + memmove(node->prefix, minKey + depth + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } + newNode->insertNode4AlwaysChangeFp(this, nodeRef, key[depth + mismatchPos], + makeLeaf(value), depth_prev); + return; + } + depth += node->prefixLength; + } + + // Recurse + ArtNode** child = findChild(node, key[depth]); + if (*child) { + fp_path[fp_path_length] = *child; // add the node to the array before recursion + fp_path_length++; // increase the size of the array + insert_recursive_always_change_fp(tree, *child, child, key, depth + 1, value, maxKeyLength); + return; + } + + // Insert leaf into inner node + ArtNode* newNode = makeLeaf(value); + switch (node->type) { + case NodeType4: + static_cast(node)->insertNode4AlwaysChangeFp(this, nodeRef, key[depth], + newNode, depth_prev); + break; + case NodeType16: + static_cast(node)->insertNode16AlwaysChangeFp(this, nodeRef, key[depth], + newNode, depth_prev); + break; + case NodeType48: + static_cast(node)->insertNode48AlwaysChangeFp(this, nodeRef, key[depth], + newNode, depth_prev); + break; + case NodeType256: + static_cast(node)->insertNode256AlwaysChangeFp(this, nodeRef, key[depth], + newNode, depth_prev); + break; + } + } + }; + +} // namespace ART \ No newline at end of file diff --git a/QuARTVariants/QuART_tail.h b/QuARTVariants/QuART_tail.h new file mode 100644 index 0000000..08b5136 --- /dev/null +++ b/QuARTVariants/QuART_tail.h @@ -0,0 +1,218 @@ +#pragma once + +#include "ART.h" +#include "ArtNode.h" + +namespace ART { + + class QuART_tail : public ART { + public: + + QuART_tail() : ART() {} + + void insert(uint8_t key[], uintptr_t value) { + + // Check if we can tail insert + ArtNode* root = this->root; + int leafValue = getLeafValue(this->fp_leaf); + // Check if the root is not null and is not a leaf + if (root != nullptr && !isLeaf(root)) { + // For each byte in the key excluding the last byte, + // check if it matches the corresponding byte in the leaf value + // If any byte does not match, set can_tail_insert to false + for (size_t i = 0; i < maxPrefixLength - 1; ++i) { + uint8_t leafByte = (leafValue >> (8 * (maxPrefixLength - 1 - i))) & 0xFF; + if (leafByte != key[i]) { + // If the key defers from leafByte earlier, we tail insert from root + std::array temp_fp_path = {this->root}; + size_t temp_fp_path_length = 1; + QuART_tail::insert_recursive_tail(this, this->root, &this->root, key, 0, value, maxPrefixLength, + temp_fp_path, temp_fp_path_length); + return; + } + } + } + else { + // If the root is null or is a leaf, we tail insert from root + std::array temp_fp_path = {this->root}; + size_t temp_fp_path_length = 1; + QuART_tail::insert_recursive_tail(this, this->root, &this->root, key, 0, value, maxPrefixLength, + temp_fp_path, temp_fp_path_length); + return; + } + + // We reached the last byte of the key, we can tail insert + if (key[maxPrefixLength -1] >= (leafValue & 0xFF)) { + // If we can tail insert, use the fast path + std::array temp_fp_path = fp_path; + size_t temp_fp_path_length = fp_path_length; + QuART_tail::insert_recursive_tail(this, this->fp, this->fp_ref, + key, fp_depth, value, maxPrefixLength, + temp_fp_path, temp_fp_path_length); + return; + } else { + // If we cannot tail insert, we will insert from root + std::array temp_fp_path = {this->root}; + size_t temp_fp_path_length = 1; + QuART_tail::insert_recursive_tail(this, this->root, &this->root, key, 0, value, maxPrefixLength, + temp_fp_path, temp_fp_path_length); + return; + } + } + private: + + void insert_recursive_tail(ART* tree, ArtNode* node, ArtNode** nodeRef, uint8_t key[], unsigned depth, + uintptr_t value, unsigned maxKeyLength, std::array& temp_fp_path, + size_t& temp_fp_path_length) { + + size_t depth_prev = depth; + + // Insert the leaf value into the tree + if (node == NULL) { + *nodeRef = makeLeaf(value); + // Adjust only fp_leaf (fp will still be null) + tree->fp_leaf = *nodeRef; + tree->fp_ref = nodeRef; + return; + } + + if (isLeaf(node)) { + // Replace leaf with Node4 and store both leaves in it + uint8_t existingKey[maxKeyLength]; + loadKey(getLeafValue(node), existingKey); + unsigned newPrefixLength = 0; + while (existingKey[depth + newPrefixLength] == + key[depth + newPrefixLength]) + newPrefixLength++; + + Node4* newNode = new Node4(); + newNode->prefixLength = newPrefixLength; + memcpy(newNode->prefix, key + depth, + min(newPrefixLength, maxPrefixLength)); + *nodeRef = newNode; + // If the changing node was the fp, push it to the temp_fp_path + if (tree->fp_leaf == node) { + temp_fp_path[temp_fp_path_length - 1] = newNode; + } + newNode->tailInsertNode4(this, nodeRef, existingKey[depth + newPrefixLength], + node, temp_fp_path, temp_fp_path_length, depth_prev); + newNode->tailInsertNode4(this, nodeRef, key[depth + newPrefixLength], + makeLeaf(value), temp_fp_path, temp_fp_path_length, depth_prev); + return; + } + + // Handle prefix of inner node + if (node->prefixLength) { + unsigned mismatchPos = prefixMismatch(node, key, depth, maxKeyLength); + if (mismatchPos != node->prefixLength) { + // Prefix differs, create new node + Node4* newNode = new Node4(); + *nodeRef = newNode; + newNode->prefixLength = mismatchPos; + memcpy(newNode->prefix, node->prefix, + min(mismatchPos, maxPrefixLength)); + // Break up prefix + if (node->prefixLength < maxPrefixLength) { + // Stores the temp_fp_path and fp_path sizes before operations + size_t temp_fp_path_length_old = temp_fp_path_length; + size_t fp_path_length_old = tree->fp_path_length; + // In all cases, newNode should be added to fp_path + temp_fp_path[temp_fp_path_length_old - 1] = newNode; + // If the nodes that being changed is in fp_path + if (temp_fp_path_length_old <= fp_path_length_old && tree->fp_path[temp_fp_path_length_old-1] == node) { + // If the new value is less than the current fp_leaf, + // restore fp_path to what it before the change with the + // newNode added + if (value < getLeafValue(tree->fp)) { + // A deep copy of remainder of fp_path + std::array fp_path_remainder; + std::copy(tree->fp_path.begin() + (temp_fp_path_length_old - 1), + tree->fp_path.begin() + fp_path_length_old, fp_path_remainder.begin()); + tree->fp_path = temp_fp_path; // update the fp path + tree->fp_path_length = temp_fp_path_length_old; + // Add the remainder of fp_path to fp_path + for (int i = 0; i < fp_path_length_old - temp_fp_path_length_old + 1; i++) { + tree->fp_path[i + temp_fp_path_length_old] = fp_path_remainder[i]; + tree->fp_path_length++; + } + } + } + newNode->tailInsertNode4(this, nodeRef, node->prefix[mismatchPos], node, + temp_fp_path, temp_fp_path_length, depth_prev); + node->prefixLength -= (mismatchPos + 1); + memmove(node->prefix, node->prefix + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } else { + node->prefixLength -= (mismatchPos + 1); + uint8_t minKey[maxKeyLength]; + loadKey(getLeafValue(minimum(node)), minKey); + // Stores the temp_fp_path and fp_path sizes before operations + size_t temp_fp_path_length_old = temp_fp_path_length; + size_t fp_path_length_old = tree->fp_path_length; + // In all cases, newNode should be added to fp_path + temp_fp_path[temp_fp_path_length_old - 1] = newNode; + // If the nodes that being changed is in fp_path + if (temp_fp_path_length_old <= fp_path_length_old && tree->fp_path[temp_fp_path_length_old-1] == node) { + // If the new value is less than the current fp_leaf, + // restore fp_path to what it before the change with the + // newNode added + if (value < getLeafValue(tree->fp)) { + // A deep copy of remainder of fp_path + std::array fp_path_remainder; + std::copy(tree->fp_path.begin() + (temp_fp_path_length_old - 1), + tree->fp_path.begin() + fp_path_length_old, fp_path_remainder.begin()); + tree->fp_path = temp_fp_path; // update the fp path + tree->fp_path_length = temp_fp_path_length_old; + // Add the remainder of fp_path to fp_path + for (int i = 0; i < fp_path_length_old - temp_fp_path_length_old + 1; i++) { + tree->fp_path[i + temp_fp_path_length_old] = fp_path_remainder[i]; + tree->fp_path_length++; + } + } + } + newNode->tailInsertNode4(this, nodeRef, minKey[depth + mismatchPos], + node, temp_fp_path, temp_fp_path_length, depth_prev); + memmove(node->prefix, minKey + depth + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } + newNode->tailInsertNode4(this, nodeRef, key[depth + mismatchPos], + makeLeaf(value), temp_fp_path, temp_fp_path_length, depth_prev); + return; + } + depth += node->prefixLength; + } + + // Recurse + ArtNode** child = findChild(node, key[depth]); + if (*child) { + temp_fp_path[temp_fp_path_length] = *child; // add the node to the array before recursion + temp_fp_path_length++; // increase the size of the array + insert_recursive_tail(tree, *child, child, key, depth + 1, value, maxKeyLength, + temp_fp_path, temp_fp_path_length); + return; + } + + // Insert leaf into inner node + ArtNode* newNode = makeLeaf(value); + switch (node->type) { + case NodeType4: + static_cast(node)->tailInsertNode4(this, nodeRef, key[depth], + newNode, temp_fp_path, temp_fp_path_length, depth_prev); + break; + case NodeType16: + static_cast(node)->tailInsertNode16(this, nodeRef, key[depth], + newNode, temp_fp_path, temp_fp_path_length, depth_prev); + break; + case NodeType48: + static_cast(node)->tailInsertNode48(this, nodeRef, key[depth], + newNode, temp_fp_path, temp_fp_path_length, depth_prev); + break; + case NodeType256: + static_cast(node)->tailInsertNode256(this, nodeRef, key[depth], + newNode, temp_fp_path, temp_fp_path_length, depth_prev); + break; + } + } + }; + +} // namespace ART \ No newline at end of file diff --git a/QuARTVariants/QuART_xtail.h b/QuARTVariants/QuART_xtail.h new file mode 100644 index 0000000..52aca7e --- /dev/null +++ b/QuARTVariants/QuART_xtail.h @@ -0,0 +1,285 @@ +#pragma once + +#include "ART.h" +#include "ArtNode.h" +#include "QuART_tail.h" + +namespace ART { + + class QuART_xtail : public ART { + public: + QuART_xtail() : ART() {} + + void insert(uint8_t key[], uintptr_t value) { + // Check if we can tail insert + ArtNode* root = this->root; + // Check if the root is not null and is not a leaf + if (root != nullptr && !isLeaf(root)) { + int leafValue = getLeafValue(this->fp_leaf); + // For each byte in the key excluding the last byte, + // check if it matches the corresponding byte in the leaf value + // If any byte does not match, set can_tail_insert to false + for (size_t i = 0; i < maxPrefixLength - 1; i++) { + uint8_t leafByte = (leafValue >> (8 * (maxPrefixLength - 1 - i))) & 0xFF; + if (key[i] == leafByte) { + continue; + } + else if (key[i] < leafByte) { + // If the key is less than the leaf value, we do insert without + // tracking the path, as this will never be the new fp path. We only + // update the current fp information if it changes. + QuART_xtail::insert_recursive_only_update_fp(this, this->root, &this->root, key, 0, value, maxPrefixLength); + return; + } + else { + // If the key is greater than the leaf value, we do tail insert with + // tracking the path and updating fp information in the end + this->fp_path = {this->root}; + this->fp_path_length = 1; + QuART_xtail::insert_recursive_always_change_fp(this, this->root, &this->root, key, 0, value, maxPrefixLength); + return; + } + } + } + else { + // If the root is null or is a leaf, we cannot tail insert + this->fp_path = {this->root}; + this->fp_path_length = 1; + QuART_xtail::insert_recursive_always_change_fp(this, this->root, &this->root, key, 0, value, maxPrefixLength); + return; + } + + QuART_xtail::insert_recursive_only_update_fp(this, this->fp, this->fp_ref, + key, fp_depth, value, maxPrefixLength); + return; + } + + private: + + void insert_recursive_only_update_fp(ART* tree, ArtNode* node, ArtNode** nodeRef, uint8_t key[], unsigned depth, + uintptr_t value, unsigned maxKeyLength) { + + // Insert the leaf value into the tree + if (node == NULL) { + *nodeRef = makeLeaf(value); + // Adjust only fp_leaf (fp will still be null) + tree->fp_leaf = *nodeRef; + tree->fp_ref = nodeRef; + return; + } + + if (isLeaf(node)) { + + // Replace leaf with Node4 and store both leaves in it + uint8_t existingKey[maxKeyLength]; + loadKey(getLeafValue(node), existingKey); + unsigned newPrefixLength = 0; + while (existingKey[depth + newPrefixLength] == + key[depth + newPrefixLength]) + newPrefixLength++; + + Node4* newNode = new Node4(); + newNode->prefixLength = newPrefixLength; + memcpy(newNode->prefix, key + depth, + min(newPrefixLength, maxPrefixLength)); + *nodeRef = newNode; + + // If the changing node was the fp just straight change the node + if (tree->fp_leaf == node) { + this->fp_path[this->fp_path_length] = newNode; + this->fp_path_length++; + this->fp = newNode; + this->fp_ref = nodeRef; + this->fp_depth = depth; + } + newNode->insertNode4(this, nodeRef, existingKey[depth + newPrefixLength], + node); + newNode->insertNode4(this, nodeRef, key[depth + newPrefixLength], + makeLeaf(value)); + return; + } + + // Handle prefix of inner node + if (node->prefixLength) { + unsigned mismatchPos = prefixMismatch(node, key, depth, maxKeyLength); + if (mismatchPos != node->prefixLength) { + // Prefix differs, create new node + Node4* newNode = new Node4(); + *nodeRef = newNode; + newNode->prefixLength = mismatchPos; + memcpy(newNode->prefix, node->prefix, + min(mismatchPos, maxPrefixLength)); + // Break up prefix + if (node->prefixLength < maxPrefixLength) { + // If the nodes that being changed is in fp_path + auto it = std::find(fp_path.begin(), fp_path.begin() + fp_path_length, node); + if (it != fp_path.begin() + fp_path_length) { + // Find the position of node in fp_path + size_t pos = std::distance(fp_path.begin(), it); + std::copy_backward(fp_path.begin() + pos, fp_path.begin() + fp_path_length, fp_path.begin() + fp_path_length + 1); + fp_path[pos] = newNode; + fp_path_length++; + } + newNode->insertNode4(this, nodeRef, node->prefix[mismatchPos], node); + node->prefixLength -= (mismatchPos + 1); + memmove(node->prefix, node->prefix + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } else { + node->prefixLength -= (mismatchPos + 1); + uint8_t minKey[maxKeyLength]; + loadKey(getLeafValue(minimum(node)), minKey); + // If the nodes that being changed is in fp_path + auto it = std::find(fp_path.begin(), fp_path.begin() + fp_path_length, node); + if (it != fp_path.begin() + fp_path_length) { + // Find the position of node in fp_path + size_t pos = std::distance(fp_path.begin(), it); + std::copy_backward(fp_path.begin() + pos, fp_path.begin() + fp_path_length, fp_path.begin() + fp_path_length + 1); + fp_path[pos] = newNode; + fp_path_length++; + } + newNode->insertNode4(this, nodeRef, minKey[depth + mismatchPos], + node); + memmove(node->prefix, minKey + depth + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } + newNode->insertNode4(this, nodeRef, key[depth + mismatchPos], + makeLeaf(value)); + return; + } + depth += node->prefixLength; + } + + // Recurse + ArtNode** child = findChild(node, key[depth]); + if (*child) { + insert_recursive_only_update_fp(tree, *child, child, key, depth + 1, value, maxKeyLength); + return; + } + + // Insert leaf into inner node + ArtNode* newNode = makeLeaf(value); + switch (node->type) { + case NodeType4: + static_cast(node)->insertNode4OnlyUpdateFp(this, nodeRef, key[depth], newNode); + break; + case NodeType16: + static_cast(node)->insertNode16OnlyUpdateFp(this, nodeRef, key[depth], newNode); + break; + case NodeType48: + static_cast(node)->insertNode48OnlyUpdateFp(this, nodeRef, key[depth], newNode); + break; + case NodeType256: + static_cast(node)->insertNode256OnlyUpdateFp(this, nodeRef, key[depth], newNode); + break; + } + } + + void insert_recursive_always_change_fp(ART* tree, ArtNode* node, ArtNode** nodeRef, uint8_t key[], unsigned depth, + uintptr_t value, unsigned maxKeyLength) { + + size_t depth_prev = depth; + + // Insert the leaf value into the tree + if (node == NULL) { + *nodeRef = makeLeaf(value); + // Adjust only fp_leaf (fp will still be null) + tree->fp_leaf = *nodeRef; + tree->fp_ref = nodeRef; + return; + } + + if (isLeaf(node)) { + // Replace leaf with Node4 and store both leaves in it + uint8_t existingKey[maxKeyLength]; + loadKey(getLeafValue(node), existingKey); + unsigned newPrefixLength = 0; + while (existingKey[depth + newPrefixLength] == + key[depth + newPrefixLength]) + newPrefixLength++; + + Node4* newNode = new Node4(); + newNode->prefixLength = newPrefixLength; + memcpy(newNode->prefix, key + depth, + min(newPrefixLength, maxPrefixLength)); + *nodeRef = newNode; + + fp_path[fp_path_length - 1] = newNode; + + newNode->insertNode4(this, nodeRef, existingKey[depth + newPrefixLength], + node); + newNode->insertNode4AlwaysChangeFp(this, nodeRef, key[depth + newPrefixLength], + makeLeaf(value), depth_prev); + return; + } + + // Handle prefix of inner node + if (node->prefixLength) { + unsigned mismatchPos = prefixMismatch(node, key, depth, maxKeyLength); + if (mismatchPos != node->prefixLength) { + // Prefix differs, create new node + Node4* newNode = new Node4(); + *nodeRef = newNode; + newNode->prefixLength = mismatchPos; + memcpy(newNode->prefix, node->prefix, + min(mismatchPos, maxPrefixLength)); + // Break up prefix + if (node->prefixLength < maxPrefixLength) { + // In all cases, newNode should be added to fp_path + fp_path[fp_path_length - 1] = newNode; + // If the nodes that being changed is in fp_path + newNode->insertNode4(this, nodeRef, node->prefix[mismatchPos], node); + node->prefixLength -= (mismatchPos + 1); + memmove(node->prefix, node->prefix + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } else { + node->prefixLength -= (mismatchPos + 1); + uint8_t minKey[maxKeyLength]; + loadKey(getLeafValue(minimum(node)), minKey); + // In all cases, newNode should be added to fp_path + fp_path[fp_path_length - 1] = newNode; + newNode->insertNode4(this, nodeRef, minKey[depth + mismatchPos], node); + memmove(node->prefix, minKey + depth + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } + newNode->insertNode4AlwaysChangeFp(this, nodeRef, key[depth + mismatchPos], + makeLeaf(value), depth_prev); + return; + } + depth += node->prefixLength; + } + + // Recurse + ArtNode** child = findChild(node, key[depth]); + if (*child) { + fp_path[fp_path_length] = *child; // add the node to the array before recursion + fp_path_length++; // increase the size of the array + insert_recursive_always_change_fp(tree, *child, child, key, depth + 1, value, maxKeyLength); + return; + } + + // Insert leaf into inner node + ArtNode* newNode = makeLeaf(value); + switch (node->type) { + case NodeType4: + static_cast(node)->insertNode4AlwaysChangeFp(this, nodeRef, key[depth], + newNode, depth_prev); + break; + case NodeType16: + static_cast(node)->insertNode16AlwaysChangeFp(this, nodeRef, key[depth], + newNode, depth_prev); + break; + case NodeType48: + static_cast(node)->insertNode48AlwaysChangeFp(this, nodeRef, key[depth], + newNode, depth_prev); + break; + case NodeType256: + static_cast(node)->insertNode256AlwaysChangeFp(this, nodeRef, key[depth], + newNode, depth_prev); + break; + } + } + + + }; + +} // namespace ART \ No newline at end of file diff --git a/README.md b/README.md index 01e58be..12b8cf6 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,108 @@ -# ART +# QuART + +This repository contains an implementation of the Adaptive Radix Tree (ART) and several QuART (Quotient Adaptive Radix Tree) variants with different fast-path strategies. + +--- ## How to Build -1. Create `build` directory and switch directories +1. Create a `build` directory and switch to it: ```shell mkdir build/ cd build/ ``` -2. Compile using `CMAKE` +2. Compile using CMake: ```shell cmake .. make ``` +--- + ## How to Run -Run the executable in `build` with the following options: +Run the executables in `build` with the following options: + ```shell ./main [-v] [-N ] -f ``` -### Understanding the Input +```shell +./profile_inserts [-N ] +``` +```shell +./profile_inserts_with_file [-N ] -f +``` +```shell +./art [-N ] -f +``` +```shell +./quart_tail [-N ] -f +``` +```shell +./quart_xtail [-N ] -f +``` +```shell +./quart_lil [-N ] -f +``` + +### Arguments + +- `-f `: Path to the binary file that contains keys +- `-N `: Number of keys to insert and query (optional, default = 5,000,000) +- `-v`: Verbose mode (optional, default = false) + +> **Note:** +> `profile_inserts` is a benchmark that only inserts and queries sorted data (10 million entries by default). It does not use a workload file and is intended for profiling the performance of bulk sorted inserts and queries. + +### Example + +```shell +./main -N 1000000 -f ../bods/workloads/workload_N1000000_K90_L10.bin +``` + +--- + +## How to Run Experiments + +To automate experiments and record results for all tree variants and workloads, use the provided shell script: + +```shell +bash run_experiments.sh +``` + +**Note:** +The script expects input files to be named in the form: +`workload_N{N}_K{K}_L{L}.bin` +and located in the `../bods/workloads/` directory (relative to the project root). + +This script will: +- Run all tree variants (ART, QuART_tail, QuART_xtail, QuART_lil) on all workload files in `../bods/workloads/` +- Repeat each experiment 7 times and record the average insertion and query times +- Save results in the `results/` directory with a timestamped filename + +### Script Output + +- Results are written to a file like `results/results_YYYYMMDD_HHMMSS.txt` +- Each line contains: + `N,K,L,type_of_tree,avg_insert_time,avg_query_time` + +--- + +## QuART Variants + +- **QuART_tail**: Standard QuART with tail optimization. +- **QuART_xtail**: QuART with extended/intelligent tail handling. +- **QuART_lil**: QuART with "lil" (lightweight/inline leaf) optimization. +- **ART**: Baseline Adaptive Radix Tree. + +You can run each variant by building and running the corresponding executable in `build/` (e.g., `./quart_tail`, `./quart_xtail`, `./quart_lil`, `./art`). + +--- + +## Notes -f: path to the binary file that contains keys +- Input files should be binary files containing 32-bit unsigned integer keys. +- You can modify `run_experiments.sh` to change the number of repetitions, workload location, or which tree variants are tested. +- Results are saved in CSV format for easy analysis. -N: number of keys to insert and query (optional, default = 5000000) -v: verbose mode (optional, default = false) diff --git a/benchmarks/art.cpp b/benchmarks/art.cpp new file mode 100644 index 0000000..e77f4a9 --- /dev/null +++ b/benchmarks/art.cpp @@ -0,0 +1,91 @@ + +#include +#include +#include +#include +#include + +#include "ART.h" +#include "ArtNode.h" +#include "ArtNodeNewMethods.cpp" +#include "Chain.h" +#include "Helper.h" + +using namespace std; + +template +std::vector read_bin(const char* filename) { + std::ifstream inputFile(filename, std::ios::binary); + inputFile.seekg(0, std::ios::end); + const std::streampos fileSize = inputFile.tellg(); + inputFile.seekg(0, std::ios::beg); + std::vector data(fileSize / sizeof(key_type)); + inputFile.read(reinterpret_cast(data.data()), fileSize); + return data; +} + +int main(int argc, char** argv) { + int N = 50000000; // optional argument + string input_file; // required argument + // Parse arguments; make sure to increment i by 2 if you consume an argument + for (int i = 1; i < argc;) { + if (string(argv[i]) == "-N") { + N = atoi(argv[i + 1]); + i += 2; + } else if (string(argv[i]) == "-f") { + input_file = argv[i + 1]; + i += 2; + } + } + + // read data + auto keys = read_bin(input_file.c_str()); + + // Build tree + ART::ART* tree = new ART::ART(); + + long long insertion_time = 0; + for (uint64_t i = 0; i < N; i++) { + uint8_t key[4]; + ART::loadKey(keys[i], key); + auto start = chrono::high_resolution_clock::now(); + + if (keys[i] == 41488038) { + + cout << "we got here debugger" << endl; + cout << "before insert: "; + //tree->printTree(); + tree->insert(key, keys[i]); + cout << "after insert: "; + //tree->printTree(); + } + else { + tree->insert(key, keys[i]); + } + + auto stop = chrono::high_resolution_clock::now(); + auto duration = + chrono::duration_cast(stop - start); + insertion_time += duration.count(); + } + + // Query tree + long long query_time = 0; + for (uint64_t i = 0; i < N; i++) { + //cout << i << endl; + uint8_t key[4]; + ART::loadKey(keys[i], key); + auto start = chrono::high_resolution_clock::now(); + ART::ArtNode* leaf = tree->lookup(key); + auto stop = chrono::high_resolution_clock::now(); + auto duration = + chrono::duration_cast(stop - start); + query_time += duration.count(); + assert(ART::isLeaf(leaf) && ART::getLeafValue(leaf) == keys[i]); + } + + // simply output the times in csv format + cout << insertion_time << "," << query_time << endl; + + return 0; +} diff --git a/benchmarks/profile_inserts.cpp b/benchmarks/profile_inserts.cpp new file mode 100644 index 0000000..ad178a6 --- /dev/null +++ b/benchmarks/profile_inserts.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include + +#include "ART.h" +#include "QuARTVariants/QuART_tail.h" +#include "QuARTVariants/QuART_xtail.h" +#include "QuARTVariants/QuART_lil.h" +#include "ArtNode.h" +#include "ArtNodeNewMethods.cpp" +#include "Chain.h" +#include "Helper.h" + +using namespace std; + +int main(int argc, char** argv) { + int N = 50000000; // default value + // Removed unused variable `input_file` + // Parse only -N argument + for (int i = 1; i < argc;) { + if (string(argv[i]) == "-N" && i + 1 < argc) { + N = atoi(argv[i + 1]); + i += 2; + } else { + i++; + } + } + + // read data + std::vector keys(10000000); + for (uint32_t i = 0; i < 10000000; i++) { + keys[i] = i + 1; + } + + + // Change the type of tree to ART::ART to use ART tree + ART::QuART_xtail* tree = new ART::QuART_xtail(); + + for (uint64_t i = 0; i < N; i++) { + uint8_t key[4]; + ART::loadKey(keys[i], key); + + tree->insert(key, keys[i]); + + // Uncomment the following lines to verify the tail path after each insertion + /* + if (!tree->verifyTailPath()) { + cout << "fp path verification failed at i=" << i << ", keys=" << keys[i] << endl; + break; + } + */ + } + + return 0; +} diff --git a/benchmarks/profile_inserts_with_file.cpp b/benchmarks/profile_inserts_with_file.cpp new file mode 100644 index 0000000..467093a --- /dev/null +++ b/benchmarks/profile_inserts_with_file.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include + +#include "ART.h" +#include "QuARTVariants/QuART_tail.h" +#include "QuARTVariants/QuART_xtail.h" +#include "QuARTVariants/QuART_lil.h" +#include "ArtNode.h" +#include "ArtNodeNewMethods.cpp" +#include "Chain.h" +#include "Helper.h" + +using namespace std; + +template +std::vector read_bin(const char* filename) { + std::ifstream inputFile(filename, std::ios::binary); + inputFile.seekg(0, std::ios::end); + const std::streampos fileSize = inputFile.tellg(); + inputFile.seekg(0, std::ios::beg); + std::vector data(fileSize / sizeof(key_type)); + inputFile.read(reinterpret_cast(data.data()), fileSize); + return data; +} + + +int main(int argc, char** argv) { + int N = 50000000; // default value + string input_file; // required argument + + for (int i = 1; i < argc;) { + if (string(argv[i]) == "-N" && i + 1 < argc) { + N = atoi(argv[i + 1]); + i += 2; + } else if (string(argv[i]) == "-f" && i + 1 < argc) { + input_file = argv[i + 1]; + i += 2; + } else { + i++; + } + } + + // read data + auto keys = read_bin(input_file.c_str()); + + // Change the type of tree to ART::ART to use ART tree + ART::QuART_xtail* tree = new ART::QuART_xtail(); + + for (uint64_t i = 0; i < N; i++) { + uint8_t key[4]; + ART::loadKey(keys[i], key); + + tree->insert(key, keys[i]); + + // Uncomment the following lines to verify the tail path after each insertion + /* + if (!tree->verifyTailPath()) { + cout << "fp path verification failed at i=" << i << ", keys=" << keys[i] << endl; + break; + } + */ + } + + return 0; +} \ No newline at end of file diff --git a/main.cpp b/benchmarks/quart_lil.cpp similarity index 82% rename from main.cpp rename to benchmarks/quart_lil.cpp index 2f90c18..e60f3d4 100644 --- a/main.cpp +++ b/benchmarks/quart_lil.cpp @@ -6,7 +6,9 @@ #include #include "ART.h" +#include "QuARTVariants/QuART_lil.h" #include "ArtNode.h" +#include "ArtNodeNewMethods.cpp" #include "Chain.h" #include "Helper.h" @@ -24,15 +26,11 @@ std::vector read_bin(const char* filename) { } int main(int argc, char** argv) { - bool verbose = false; // optional argument - int N = 5000000; // optional argument + int N = 50000000; // optional argument string input_file; // required argument // Parse arguments; make sure to increment i by 2 if you consume an argument for (int i = 1; i < argc;) { - if (string(argv[i]) == "-v") { - verbose = true; - i++; - } else if (string(argv[i]) == "-N") { + if (string(argv[i]) == "-N") { N = atoi(argv[i + 1]); i += 2; } else if (string(argv[i]) == "-f") { @@ -45,27 +43,26 @@ int main(int argc, char** argv) { auto keys = read_bin(input_file.c_str()); // Build tree + ART::QuART_lil* tree = new ART::QuART_lil(); - ART::ART* tree = new ART::ART(); long long insertion_time = 0; for (uint64_t i = 0; i < N; i++) { uint8_t key[4]; ART::loadKey(keys[i], key); auto start = chrono::high_resolution_clock::now(); + tree->insert(key, keys[i]); + auto stop = chrono::high_resolution_clock::now(); auto duration = chrono::duration_cast(stop - start); insertion_time += duration.count(); } - if (verbose) { - cout << "Insertion time: " << insertion_time << " ns" << endl; - } - // Query tree long long query_time = 0; for (uint64_t i = 0; i < N; i++) { + //cout << i << endl; uint8_t key[4]; ART::loadKey(keys[i], key); auto start = chrono::high_resolution_clock::now(); @@ -76,11 +73,7 @@ int main(int argc, char** argv) { query_time += duration.count(); assert(ART::isLeaf(leaf) && ART::getLeafValue(leaf) == keys[i]); } - - if (verbose) { - cout << "Query time: " << query_time << " ns" << endl; - } - + // simply output the times in csv format cout << insertion_time << "," << query_time << endl; diff --git a/benchmarks/quart_tail.cpp b/benchmarks/quart_tail.cpp new file mode 100644 index 0000000..4491cd6 --- /dev/null +++ b/benchmarks/quart_tail.cpp @@ -0,0 +1,81 @@ + +#include +#include +#include +#include +#include + +#include "ART.h" +#include "QuARTVariants/QuART_tail.h" +#include "ArtNode.h" +#include "ArtNodeNewMethods.cpp" +#include "Chain.h" +#include "Helper.h" + +using namespace std; + +template +std::vector read_bin(const char* filename) { + std::ifstream inputFile(filename, std::ios::binary); + inputFile.seekg(0, std::ios::end); + const std::streampos fileSize = inputFile.tellg(); + inputFile.seekg(0, std::ios::beg); + std::vector data(fileSize / sizeof(key_type)); + inputFile.read(reinterpret_cast(data.data()), fileSize); + return data; +} + +int main(int argc, char** argv) { + int N = 50000000; // optional argument + string input_file; // required argument + // Parse arguments; make sure to increment i by 2 if you consume an argument + for (int i = 1; i < argc;) { + if (string(argv[i]) == "-N") { + N = atoi(argv[i + 1]); + i += 2; + } else if (string(argv[i]) == "-f") { + input_file = argv[i + 1]; + i += 2; + } + } + + // read data + auto keys = read_bin(input_file.c_str()); + + // Build tree + ART::QuART_tail* tree = new ART::QuART_tail(); + + long long insertion_time = 0; + for (uint64_t i = 0; i < N; i++) { + uint8_t key[4]; + ART::loadKey(keys[i], key); + auto start = chrono::high_resolution_clock::now(); + + tree->insert(key, keys[i]); + + auto stop = chrono::high_resolution_clock::now(); + auto duration = + chrono::duration_cast(stop - start); + insertion_time += duration.count(); + } + + // Query tree + long long query_time = 0; + for (uint64_t i = 0; i < N; i++) { + //cout << i << endl; + uint8_t key[4]; + ART::loadKey(keys[i], key); + auto start = chrono::high_resolution_clock::now(); + ART::ArtNode* leaf = tree->lookup(key); + auto stop = chrono::high_resolution_clock::now(); + auto duration = + chrono::duration_cast(stop - start); + query_time += duration.count(); + assert(ART::isLeaf(leaf) && ART::getLeafValue(leaf) == keys[i]); + } + + // simply output the times in csv format + cout << insertion_time << "," << query_time << endl; + + return 0; +} diff --git a/benchmarks/quart_xtail.cpp b/benchmarks/quart_xtail.cpp new file mode 100644 index 0000000..2cfff2f --- /dev/null +++ b/benchmarks/quart_xtail.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +#include "ART.h" +#include "QuARTVariants/QuART_xtail.h" +#include "ArtNode.h" +#include "ArtNodeNewMethods.cpp" +#include "Chain.h" +#include "Helper.h" + +using namespace std; + +template +std::vector read_bin(const char* filename) { + std::ifstream inputFile(filename, std::ios::binary); + inputFile.seekg(0, std::ios::end); + const std::streampos fileSize = inputFile.tellg(); + inputFile.seekg(0, std::ios::beg); + std::vector data(fileSize / sizeof(key_type)); + inputFile.read(reinterpret_cast(data.data()), fileSize); + return data; +} + +int main(int argc, char** argv) { + int N = 50000000; // optional argument + string input_file; // required argument + // Parse arguments; make sure to increment i by 2 if you consume an argument + for (int i = 1; i < argc;) { + if (string(argv[i]) == "-N") { + N = atoi(argv[i + 1]); + i += 2; + } else if (string(argv[i]) == "-f") { + input_file = argv[i + 1]; + i += 2; + } + } + + // read data + auto keys = read_bin(input_file.c_str()); + + // Build tree + ART::QuART_xtail* tree = new ART::QuART_xtail(); + + long long insertion_time = 0; + for (uint64_t i = 0; i < N; i++) { + uint8_t key[4]; + ART::loadKey(keys[i], key); + auto start = chrono::high_resolution_clock::now(); + + //if (41487881 <= keys[i] && keys[i] <= 41488107) { + //if (keys[i] == 41487916) { + // 41488038 + if (keys[i] == 41488038) { + + cout << "we got here debugger" << endl; + cout << "before insert: "; + //tree->printTree(); + tree->insert(key, keys[i]); + cout << "after insert: "; + //tree->printTree(); + } + else { + tree->insert(key, keys[i]); + } + + auto stop = chrono::high_resolution_clock::now(); + auto duration = + chrono::duration_cast(stop - start); + insertion_time += duration.count(); + + if (i != 0 && i != 1) { + if (!tree->verifyTailPath()) { + cout << "fp path verification failed at i=" << i << ", keys=" << keys[i] << endl; + break; + } + } + + } + + // Query tree + long long query_time = 0; + for (uint64_t i = 0; i < N; i++) { + //cout << i << endl; + uint8_t key[4]; + ART::loadKey(keys[i], key); + auto start = chrono::high_resolution_clock::now(); + ART::ArtNode* leaf = tree->lookup(key); + auto stop = chrono::high_resolution_clock::now(); + auto duration = + chrono::duration_cast(stop - start); + query_time += duration.count(); + + if (!(ART::isLeaf(leaf) && ART::getLeafValue(leaf) == keys[i])) { + std::cerr << "Assertion failed at i=" << i << std::endl; + std::cerr << "Expected key (uint32_t): " << keys[i] << std::endl; + std::cerr << "Expected key bytes: "; + for (int j = 3; j >= 0; --j) std::cerr << ((keys[i] >> (8*j)) & 0xFF) << " "; + std::cerr << std::endl; + + uint32_t leafval = ART::getLeafValue(leaf); + std::cerr << "Actual leaf value (uint32_t): " << leafval << std::endl; + std::cerr << "Actual leaf bytes: "; + for (int j = 3; j >= 0; --j) std::cerr << ((leafval >> (8*j)) & 0xFF) << " "; + std::cerr << std::endl; + + std::cerr << "Key used for lookup: "; + for (int j = 0; j < 4; ++j) std::cerr << (int)key[j] << " "; + std::cerr << std::endl; + + //tree->printTree(); + abort(); + } + } + + // simply output the times in csv format + cout << insertion_time << "," << query_time << endl; + + return 0; +} diff --git a/test_range_query.cpp b/benchmarks/test_range_query.cpp similarity index 100% rename from test_range_query.cpp rename to benchmarks/test_range_query.cpp diff --git a/perf.data b/perf.data new file mode 100644 index 0000000..26a47a7 Binary files /dev/null and b/perf.data differ diff --git a/run_experiments.sh b/run_experiments.sh new file mode 100755 index 0000000..4071649 --- /dev/null +++ b/run_experiments.sh @@ -0,0 +1,41 @@ +#!/bin/bash +SUFFIX=$(date +"%Y%m%d_%H%M%S") +RESULTSDIR="results" +RESULTS="${RESULTSDIR}/results_${SUFFIX}.txt" +LOGDIR="${RESULTSDIR}/logs" + +mkdir -p "$LOGDIR" + +echo "N,K,L,type_of_tree,avg_insert_time,avg_query_time" > "$RESULTS" + +REPEAT=1 + +for FILE in ../bods/workloads/workload_N*_K*_L*.bin; do + [ -f "$FILE" ] || continue + + BASENAME=$(basename "$FILE") + N=$(echo "$BASENAME" | sed -n 's/.*_N\([0-9]*\)_K[0-9]*_L[0-9]*.bin/\1/p') + K=$(echo "$BASENAME" | sed -n 's/.*_N[0-9]*_K\([0-9]*\)_L[0-9]*.bin/\1/p') + L=$(echo "$BASENAME" | sed -n 's/.*_N[0-9]*_K[0-9]*_L\([0-9]*\).bin/\1/p') + LOGFILE="${LOGDIR}/log_${BASENAME%.txt}_${SUFFIX}.txt" + + for TREE in ART QuART_tail QuART_xtail QuART_lil; do + INSERT_SUM=0 + QUERY_SUM=0 + + for ((i=1; i<=REPEAT; i++)); do + echo "Running $TREE on $FILE (run $i/$REPEAT)" >> "$LOGFILE" + OUTPUT=$(./build/$(echo $TREE | tr '[:upper:]' '[:lower:]') -f "$FILE" 2>>"$LOGFILE") + echo "$OUTPUT" >> "$LOGFILE" + CSV_LINE=$(echo "$OUTPUT" | tail -1) + INSERT_TIME=$(echo "$CSV_LINE" | cut -d',' -f1 | xargs) + QUERY_TIME=$(echo "$CSV_LINE" | cut -d',' -f2 | xargs) + INSERT_SUM=$((INSERT_SUM + INSERT_TIME)) + QUERY_SUM=$((QUERY_SUM + QUERY_TIME)) + done + + AVG_INSERT_TIME=$((INSERT_SUM / REPEAT)) + AVG_QUERY_TIME=$((QUERY_SUM / REPEAT)) + echo "$N,$K,$L,$TREE,$AVG_INSERT_TIME,$AVG_QUERY_TIME" >> "$RESULTS" + done +done \ No newline at end of file