Skip to content

Commit

Permalink
perf: improved b+tree operation performance and size
Browse files Browse the repository at this point in the history
tests are passing
  • Loading branch information
sepgh committed May 17, 2024
1 parent 994f020 commit a668df1
Show file tree
Hide file tree
Showing 15 changed files with 230 additions and 233 deletions.
57 changes: 56 additions & 1 deletion scripts/descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,60 @@ def parse_leaf(binary: str, degree: int):
print("'%-10s'" % f"{offset}-...", "", ' '.join([a[i:i+4] for i in range(0, len(a), 4)]))


def parse_internal(binary: str, degree: int):
print()
print("'%-10s'" % "0-2", "", binary[0:2])
print()

offset = 2

# First child pointer
print(
"'%-10s'" % f"{offset}-{offset+26}",
"",
binary[offset: offset + 2],
binary[offset + 2: offset + 18],
binary[offset + 18: offset + 26],
)

offset += 26

for i in range(0, degree - 1):
print(
"'%-10s'" % f"{offset}-{offset+16}",
"",
binary[offset: offset + 16],
)
print(
"'%-10s'" % f"{offset+16}-{offset+42}",
"",
binary[offset + 16: offset + 18],
binary[offset + 18: offset + 34],
binary[offset + 34: offset + 42],
)
offset += 42

print()
a = binary[offset:]
print("'%-10s'" % f"{offset}-...", "", ' '.join([a[i:i+4] for i in range(0, len(a), 4)]))


def is_leaf() -> bool:
_in = input("Leaf? y/n ")
if _in == "y" or _in == "Y":
return True
if _in == "n" or _in == "N":
return False
print("Answer with Y or N")
return is_leaf()


if __name__ == "__main__":
parse_leaf(sys.argv[2], int(sys.argv[1]))
is_leaf_node = is_leaf()
degree = int(input("degree: "))
data = input("data: ")
if is_leaf_node:
parse_leaf(data, degree)
else:
parse_internal(data, degree)

4 changes: 2 additions & 2 deletions src/main/java/com/github/sepgh/internal/EngineConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public int getPaddedSize(){
if (this.cachedPaddingSize != null){
return this.cachedPaddingSize;
}
int i = (this.bTreeNodeSize() + 8) % 8;
int i = this.bTreeNodeSize() % 8;
if (i == 0){
cachedPaddingSize = this.bTreeNodeSize();
return cachedPaddingSize;
Expand All @@ -55,7 +55,7 @@ public int indexGrowthAllocationSize() {
}

public int bTreeNodeSize(){
return 1 + (this.getBTreeNodeMaxKey() * (Long.BYTES + Pointer.BYTES)) + (3 * Pointer.BYTES);
return 1 + (this.getBTreeNodeMaxKey() * (Long.BYTES + Pointer.BYTES)) + (2 * Pointer.BYTES);
}

public static class Default {
Expand Down
25 changes: 13 additions & 12 deletions src/main/java/com/github/sepgh/internal/tree/BTreeIndexManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import com.github.sepgh.internal.tree.node.LeafTreeNode;

import java.io.IOException;
import java.util.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;

public class BTreeIndexManager implements IndexManager {
Expand Down Expand Up @@ -40,8 +42,8 @@ public BaseTreeNode addIndex(int table, long identifier, Pointer pointer) throws
/* current node is a leaf which should handle storing the data */

/* If current node has space, store and exit */
if (currentNode.getKeyList().size() < (degree - 1)){
((LeafTreeNode) currentNode).addKeyValue(identifier, pointer);
if (currentNode.getKeyList(degree).size() < (degree - 1)){
((LeafTreeNode) currentNode).addKeyValue(identifier, pointer, degree);
TreeNodeIO.write(currentNode, indexStorageManager, table).get();
return currentNode;
}
Expand All @@ -50,15 +52,13 @@ public BaseTreeNode addIndex(int table, long identifier, Pointer pointer) throws
LeafTreeNode newSiblingLeafNode = new LeafTreeNode(indexStorageManager.getEmptyNode());
List<LeafTreeNode.KeyValue> passingKeyValues = ((LeafTreeNode) currentNode).split(identifier, pointer, degree);
newSiblingLeafNode.setKeyValues(passingKeyValues, degree);
System.out.println("Passed to new sibling leaf: " + passingKeyValues);
TreeNodeIO.write(newSiblingLeafNode, indexStorageManager, table).get(); // we want the node to have a pointer so that we can fix siblings
System.out.println("Location of new sibling leaf: " + newSiblingLeafNode.getPointer());
/* Fix sibling pointers */
fixSiblingPointers((LeafTreeNode) currentNode, newSiblingLeafNode, table);
TreeNodeIO.write(newSiblingLeafNode, indexStorageManager, table).get();
TreeNodeIO.write(currentNode, indexStorageManager, table).get();

answer = currentNode.getKeyList().contains(identifier) ? currentNode : newSiblingLeafNode;
answer = currentNode.getKeyList(degree).contains(identifier) ? currentNode : newSiblingLeafNode;

/* this leaf doesn't have a parent! create one and deal with it right here! */
if (path.size() == 1) {
Expand All @@ -70,6 +70,7 @@ public BaseTreeNode addIndex(int table, long identifier, Pointer pointer) throws
passingKeyValues.get(0).key(),
currentNode.getPointer(),
newSiblingLeafNode.getPointer(),
degree,
false
);
TreeNodeIO.write(newRoot, indexStorageManager, table).get();
Expand All @@ -85,9 +86,9 @@ public BaseTreeNode addIndex(int table, long identifier, Pointer pointer) throws
/* current node is an internal node */

InternalTreeNode currentInternalTreeNode = (InternalTreeNode) currentNode;
if (currentInternalTreeNode.getKeyList().size() < degree - 1) {
if (currentInternalTreeNode.getKeyList(degree).size() < degree - 1) {
/* current internal node can store the key */
currentInternalTreeNode.addKeyPointers(idForParentToStore, null, newChildForParent.getPointer(), false);
currentInternalTreeNode.addKeyPointers(idForParentToStore, null, newChildForParent.getPointer(), degree, false);
TreeNodeIO.write(currentInternalTreeNode, indexStorageManager, table).get();
return answer;
}
Expand All @@ -114,6 +115,7 @@ public BaseTreeNode addIndex(int table, long identifier, Pointer pointer) throws
idForParentToStore,
currentNode.getPointer(),
newInternalSibling.getPointer(),
degree,
false
);
TreeNodeIO.write(newRoot, indexStorageManager, table).get();
Expand All @@ -126,15 +128,14 @@ public BaseTreeNode addIndex(int table, long identifier, Pointer pointer) throws
}
}


throw new RuntimeException("Logic error: probably failed to store index?");
}

@Override
public Optional<Pointer> getIndex(int table, long identifier) throws ExecutionException, InterruptedException {
Optional<LeafTreeNode> optionalBaseTreeNode = this.getResponsibleNode(getRoot(table), identifier, table);
if (optionalBaseTreeNode.isPresent()){
for (LeafTreeNode.KeyValue entry : optionalBaseTreeNode.get().getKeyValueList()) {
for (LeafTreeNode.KeyValue entry : optionalBaseTreeNode.get().getKeyValueList(degree)) {
if (entry.key() == identifier)
return Optional.of(entry.value());
}
Expand Down Expand Up @@ -179,7 +180,7 @@ private Optional<LeafTreeNode> getResponsibleNode(BaseTreeNode node, long identi
if (node.isLeaf()){
return Optional.of((LeafTreeNode) node);
}
List<InternalTreeNode.KeyPointers> keyPointersList = ((InternalTreeNode) node).getKeyPointersList();
List<InternalTreeNode.KeyPointers> keyPointersList = ((InternalTreeNode) node).getKeyPointersList(degree);
for (int i = 0; i < keyPointersList.size(); i++){
InternalTreeNode.KeyPointers keyPointers = keyPointersList.get(i);
if (keyPointers.getKey() > identifier && keyPointers.getLeft() != null){
Expand All @@ -206,7 +207,7 @@ private void getPathToResponsibleNode(List<BaseTreeNode> path, BaseTreeNode node
return;
}

List<InternalTreeNode.KeyPointers> keyPointersList = ((InternalTreeNode) node).getKeyPointersList();
List<InternalTreeNode.KeyPointers> keyPointersList = ((InternalTreeNode) node).getKeyPointersList(degree);
for (int i = 0; i < keyPointersList.size(); i++){
InternalTreeNode.KeyPointers keyPointers = keyPointersList.get(i);
if (keyPointers.getKey() > identifier && keyPointers.getLeft() != null){
Expand Down
102 changes: 49 additions & 53 deletions src/main/java/com/github/sepgh/internal/tree/TreeNodeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
import com.github.sepgh.internal.tree.node.BaseTreeNode;
import com.github.sepgh.internal.tree.node.InternalTreeNode;
import com.github.sepgh.internal.utils.BinaryUtils;
import com.google.common.hash.HashCode;
import com.google.common.primitives.Longs;

import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;

Expand Down Expand Up @@ -111,7 +109,10 @@ private static int getKeyStartOffset(BaseTreeNode treeNode, int index) {
* @param index of the key to check existence
* @return boolean state of existence of a key in index
*/
public static boolean hasKeyAtIndex(BaseTreeNode treeNode, int index){
public static boolean hasKeyAtIndex(BaseTreeNode treeNode, int index, int degree){
if (index >= degree - 1)
return false;

int keyStartIndex = getKeyStartOffset(treeNode, index);
if (keyStartIndex + Long.BYTES > treeNode.getData().length)
return false;
Expand Down Expand Up @@ -191,22 +192,26 @@ public static void setKeyValueAtIndex(BaseTreeNode treeNode, int index, long key
* binary search could be used
* alternatively, we can hold a space for metadata which keeps track of the number of keys or values stored
*/
public static int addKeyValueAndGetIndex(BaseTreeNode treeNode, long key, Pointer pointer) {
public static int addKeyValueAndGetIndex(BaseTreeNode treeNode, long key, Pointer pointer, int degree) {
int indexToFill = -1;
for (int i = 0; i < ((treeNode.getData().length - 1 - (2 * Pointer.BYTES)) / (Long.BYTES + Pointer.BYTES)); i++){
long keyAtIndex = getKeyAtIndex(treeNode, i);

long keyAtIndex = 0;
for (int i = 0; i < degree - 1; i++){
keyAtIndex = getKeyAtIndex(treeNode, i);
if (keyAtIndex == 0 || keyAtIndex > key){
indexToFill = i;
break;
}
}

if (indexToFill == -1){
return -1;
throw new RuntimeException("F..ed up"); // Todo
}

byte[] temp = new byte[treeNode.getData().length - (OFFSET_LEAF_NODE_KEY_BEGIN + (indexToFill * (SIZE_LEAF_NODE_KEY_POINTER))) - (2*Pointer.BYTES)];

int max = degree - 1;
int bufferSize = ((max - indexToFill - 1) * SIZE_LEAF_NODE_KEY_POINTER);

byte[] temp = new byte[bufferSize];
System.arraycopy(
treeNode.getData(),
OFFSET_LEAF_NODE_KEY_BEGIN + (indexToFill * (SIZE_LEAF_NODE_KEY_POINTER)),
Expand All @@ -221,8 +226,8 @@ public static int addKeyValueAndGetIndex(BaseTreeNode treeNode, long key, Pointe
temp,
0,
treeNode.getData(),
OFFSET_TREE_NODE_FLAGS_END + ((indexToFill + 1) * (SIZE_LEAF_NODE_KEY_POINTER)),
temp.length - SIZE_LEAF_NODE_KEY_POINTER
OFFSET_LEAF_NODE_KEY_BEGIN + ((indexToFill + 1) * (SIZE_LEAF_NODE_KEY_POINTER)),
temp.length
);


Expand Down Expand Up @@ -269,62 +274,62 @@ public static void removeKeyValueAtIndex(BaseTreeNode treeNode, int index) {
* binary search could be used
* alternatively, we can hold a space for metadata which keeps track of the number of keys or values stored
*/
public static int addKeyAndGetIndex(BaseTreeNode treeNode, long key) {
public static int addKeyAndGetIndex(BaseTreeNode treeNode, long key, int degree) {
// Shall only be called on internal nodes
if (treeNode.isLeaf()){
throw new RuntimeException(); // Todo: not runtime
}

int indexToFill = 0;
long keyAtIndex = 0;
for (int i = 0; i < ((treeNode.getData().length - 1) / (Long.BYTES + Pointer.BYTES)); i++){
for (int i = 0; i < degree - 1; i++){
keyAtIndex = getKeyAtIndex(treeNode, i);

if (keyAtIndex == 0 || key < keyAtIndex){
if (keyAtIndex == 0 || keyAtIndex > key){
indexToFill = i;
break;
}

}

if (keyAtIndex == 0){
System.arraycopy(
Longs.toByteArray(key),
0,
treeNode.getData(),
OFFSET_INTERNAL_NODE_KEY_BEGIN + (indexToFill * (SIZE_LEAF_NODE_KEY_POINTER)),
Long.BYTES
);
} else {
// Copy existing bytes to a temporary location
byte[] temp = new byte[treeNode.getData().length - (OFFSET_INTERNAL_NODE_KEY_BEGIN + (indexToFill * (SIZE_LEAF_NODE_KEY_POINTER)))];
System.arraycopy(
treeNode.getData(),
OFFSET_INTERNAL_NODE_KEY_BEGIN + (indexToFill * (SIZE_INTERNAL_NODE_KEY_POINTER)),
temp,
0,
temp.length
);

// Write
System.arraycopy(
Longs.toByteArray(key),
0,
treeNode.getData(),
OFFSET_INTERNAL_NODE_KEY_BEGIN + (indexToFill * (SIZE_INTERNAL_NODE_KEY_POINTER)),
Long.BYTES
);
return indexToFill;
}

// Copy temp bytes back to valid position
System.arraycopy(
temp,
0,
treeNode.getData(),
OFFSET_INTERNAL_NODE_KEY_BEGIN + ((indexToFill + 1) * (SIZE_INTERNAL_NODE_KEY_POINTER)),
temp.length - SIZE_INTERNAL_NODE_KEY_POINTER
);

}
int max = degree - 1;
int fullSize = (max * SIZE_INTERNAL_NODE_KEY_POINTER) + OFFSET_INTERNAL_NODE_KEY_BEGIN;
int bufferSize = fullSize - ((indexToFill + 1) * SIZE_INTERNAL_NODE_KEY_POINTER) - OFFSET_INTERNAL_NODE_KEY_BEGIN;

byte[] temp = new byte[bufferSize];
System.arraycopy(
treeNode.getData(),
OFFSET_INTERNAL_NODE_KEY_BEGIN + (indexToFill * SIZE_INTERNAL_NODE_KEY_POINTER),
temp,
0,
temp.length
);

System.arraycopy(
Longs.toByteArray(key),
0,
treeNode.getData(),
OFFSET_INTERNAL_NODE_KEY_BEGIN + (indexToFill * SIZE_INTERNAL_NODE_KEY_POINTER),
Long.BYTES
);

System.arraycopy(
temp,
0,
treeNode.getData(),
OFFSET_INTERNAL_NODE_KEY_BEGIN + ((indexToFill + 1) * SIZE_INTERNAL_NODE_KEY_POINTER),
temp.length
);

return indexToFill;
}
Expand All @@ -350,10 +355,6 @@ public static void setPreviousPointer(BaseTreeNode treeNode, int degree, Pointer
}

public static Optional<Pointer> getNextPointer(BaseTreeNode treeNode, int degree) {
System.out.println("getNextPointer is called on " + treeNode.getPointer() + " with keys:" + treeNode.getKeyList());
System.out.println("get results: " + HashCode.fromBytes(Arrays.copyOfRange(treeNode.getData(), OFFSET_LEAF_NODE_KEY_BEGIN + ((degree - 1) * SIZE_LEAF_NODE_KEY_POINTER) + Pointer.BYTES, OFFSET_LEAF_NODE_KEY_BEGIN + ((degree - 1) * SIZE_LEAF_NODE_KEY_POINTER) + Pointer.BYTES + Pointer.BYTES)));
System.out.println("get results full: " + HashCode.fromBytes(treeNode.getData()));

if (treeNode.getData()[OFFSET_LEAF_NODE_KEY_BEGIN + ((degree - 1) * SIZE_LEAF_NODE_KEY_POINTER) + Pointer.BYTES] == (byte) 0x0){
return Optional.empty();
}
Expand All @@ -364,18 +365,13 @@ public static Optional<Pointer> getNextPointer(BaseTreeNode treeNode, int degree
}

public static void setNextPointer(BaseTreeNode treeNode, int degree, Pointer pointer) {
System.out.println("Called setNextPointer on node: " + treeNode.getPointer() + " to set " + pointer);
System.arraycopy(
pointer.toBytes(),
0,
treeNode.getData(),
OFFSET_LEAF_NODE_KEY_BEGIN + ((degree - 1) * SIZE_LEAF_NODE_KEY_POINTER) + Pointer.BYTES,
Pointer.BYTES
);
System.out.println("pointer as bytes " + HashCode.fromBytes(pointer.toBytes()));
System.out.println("OFFSET_LEAF_NODE_KEY_BEGIN: " + OFFSET_LEAF_NODE_KEY_BEGIN + ", SIZE_LEAF_NODE_KEY_POINTER: " + SIZE_LEAF_NODE_KEY_POINTER + ", Pointer.BYTES: " + Pointer.BYTES + ", DEGREE: " + degree);
System.out.println("Location to copy:" + (OFFSET_LEAF_NODE_KEY_BEGIN + ((degree - 1) * SIZE_LEAF_NODE_KEY_POINTER) + Pointer.BYTES));
System.out.println("Post arraycopy results: " + HashCode.fromBytes(Arrays.copyOfRange(treeNode.getData(), OFFSET_LEAF_NODE_KEY_BEGIN + ((degree - 1) * SIZE_LEAF_NODE_KEY_POINTER) + Pointer.BYTES, OFFSET_LEAF_NODE_KEY_BEGIN + ((degree - 1) * SIZE_LEAF_NODE_KEY_POINTER) + Pointer.BYTES + Pointer.BYTES)));
}

public static void cleanChildrenPointers(InternalTreeNode treeNode, int degree) {
Expand Down
Loading

0 comments on commit a668df1

Please sign in to comment.