Skip to content

Commit fe6159b

Browse files
committed
Add allocate down implementation
1 parent a71f291 commit fe6159b

File tree

4 files changed

+159
-8
lines changed

4 files changed

+159
-8
lines changed

Diff for: src/include/storage/free_space_manager.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <optional>
4+
#include <set>
45

56
#include "common/types/types.h"
67
namespace kuzu::storage {
@@ -9,7 +10,10 @@ struct BlockEntry;
910

1011
class FreeSpaceManager {
1112
public:
13+
static bool entryCmp(const BlockEntry& a, const BlockEntry& b);
14+
1215
using free_list_t = std::vector<BlockEntry>;
16+
using sorted_free_list_t = std::set<BlockEntry, decltype(&entryCmp)>;
1317

1418
FreeSpaceManager();
1519

@@ -31,7 +35,8 @@ class FreeSpaceManager {
3135
BlockEntry breakUpChunk(BlockEntry chunk, common::page_idx_t numRequiredPages);
3236
void combineAdjacentChunks();
3337

34-
std::vector<free_list_t> freeLists;
38+
std::vector<sorted_free_list_t> freeLists;
39+
// std::vector<free_list_t> freeLists;
3540
free_list_t uncheckpointedFreeChunks;
3641
};
3742

Diff for: src/storage/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ add_subdirectory(wal)
1010

1111
add_library(kuzu_storage
1212
OBJECT
13-
free_space_manager.cpp
13+
free_space_manager_sorted.cpp
1414
block_manager.cpp
1515
db_file_id.cpp
1616
file_handle.cpp

Diff for: src/storage/block_manager.cpp

+8-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ AllocatedBlockEntry BlockManager::allocateBlock(common::page_idx_t numPages) {
1111
if constexpr (ENABLE_FSM) {
1212
common::UniqLock lck{mtx};
1313
// TODO(Royi) check if this is still needed
14-
numPages = numPages == 0 ? 0 : std::bit_ceil(numPages);
14+
// numPages = numPages == 0 ? 0 : std::bit_ceil(numPages);
1515

1616
auto allocatedFreeChunk = freeSpaceManager->getFreeChunk(numPages);
1717
if (allocatedFreeChunk.has_value()) {
@@ -24,12 +24,14 @@ AllocatedBlockEntry BlockManager::allocateBlock(common::page_idx_t numPages) {
2424
}
2525

2626
void BlockManager::freeBlock(BlockEntry entry) {
27-
common::UniqLock lck{mtx};
28-
for (uint64_t i = 0; i < entry.numPages; ++i) {
29-
const auto pageIdx = entry.startPageIdx + i;
30-
dataFH->removePageFromFrameIfNecessary(pageIdx);
27+
if constexpr (ENABLE_FSM) {
28+
common::UniqLock lck{mtx};
29+
for (uint64_t i = 0; i < entry.numPages; ++i) {
30+
const auto pageIdx = entry.startPageIdx + i;
31+
dataFH->removePageFromFrameIfNecessary(pageIdx);
32+
}
33+
freeSpaceManager->addFreeChunk(entry);
3134
}
32-
freeSpaceManager->addFreeChunk(entry);
3335
}
3436

3537
void BlockManager::addUncheckpointedFreeBlock(BlockEntry entry) {

Diff for: src/storage/free_space_manager_sorted.cpp

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#include "common/utils.h"
2+
#include "storage/block_manager.h"
3+
#include "storage/free_space_manager.h"
4+
5+
namespace kuzu::storage {
6+
static FreeSpaceManager::sorted_free_list_t& getFreeList(
7+
std::vector<FreeSpaceManager::sorted_free_list_t>& freeLists, common::idx_t level) {
8+
if (level >= freeLists.size()) {
9+
freeLists.resize(level + 1,
10+
FreeSpaceManager::sorted_free_list_t{&FreeSpaceManager::entryCmp});
11+
}
12+
return freeLists[level];
13+
}
14+
15+
FreeSpaceManager::FreeSpaceManager() : freeLists{} {};
16+
17+
bool FreeSpaceManager::entryCmp(const BlockEntry& a, const BlockEntry& b) {
18+
return a.numPages == b.numPages ? a.startPageIdx < b.startPageIdx : a.numPages < b.numPages;
19+
}
20+
21+
void FreeSpaceManager::addFreeChunk(BlockEntry entry) {
22+
const auto curLevel = common::countBits(entry.numPages) -
23+
common::CountZeros<decltype(entry.numPages)>::Leading(entry.numPages) - 1;
24+
getFreeList(freeLists, curLevel).insert(entry);
25+
}
26+
27+
void FreeSpaceManager::addUncheckpointedFreeChunk(BlockEntry entry) {
28+
uncheckpointedFreeChunks.push_back(entry);
29+
}
30+
31+
// This also removes the chunk from the free space manager
32+
std::optional<BlockEntry> FreeSpaceManager::getFreeChunk(common::page_idx_t numPages) {
33+
if (numPages > 0) {
34+
// 0b10 -> start at level 1
35+
// 0b11 -> start at level 1
36+
auto levelToSearch = common::countBits(numPages) -
37+
common::CountZeros<decltype(numPages)>::Leading(numPages) - 1;
38+
for (; levelToSearch < freeLists.size(); ++levelToSearch) {
39+
auto& curList = freeLists[levelToSearch];
40+
auto entryIt = curList.lower_bound(BlockEntry{0, numPages});
41+
if (entryIt != curList.end()) {
42+
auto entry = *entryIt;
43+
curList.erase(entry);
44+
return breakUpChunk(entry, numPages);
45+
}
46+
}
47+
}
48+
return std::nullopt;
49+
}
50+
51+
BlockEntry FreeSpaceManager::breakUpChunk(BlockEntry chunk, common::page_idx_t numRequiredPages) {
52+
KU_ASSERT(chunk.numPages >= numRequiredPages);
53+
BlockEntry ret{chunk.startPageIdx, numRequiredPages};
54+
if (numRequiredPages < chunk.numPages) {
55+
BlockEntry remainingEntry{chunk.startPageIdx + numRequiredPages,
56+
chunk.numPages - numRequiredPages};
57+
addFreeChunk(remainingEntry);
58+
}
59+
return ret;
60+
}
61+
62+
void FreeSpaceManager::serialize(common::Serializer&) {}
63+
64+
std::unique_ptr<FreeSpaceManager> FreeSpaceManager::deserialize(common::Deserializer&) {
65+
return std::make_unique<FreeSpaceManager>();
66+
}
67+
68+
void FreeSpaceManager::finalizeCheckpoint() {
69+
for (auto uncheckpointedEntry : uncheckpointedFreeChunks) {
70+
addFreeChunk(uncheckpointedEntry);
71+
}
72+
uncheckpointedFreeChunks.clear();
73+
74+
combineAdjacentChunks();
75+
}
76+
77+
void FreeSpaceManager::combineAdjacentChunks() {
78+
std::vector<BlockEntry> allEntries;
79+
for (const auto& freeList : freeLists) {
80+
allEntries.insert(allEntries.end(), freeList.begin(), freeList.end());
81+
}
82+
if (allEntries.empty()) {
83+
return;
84+
}
85+
86+
freeLists.clear();
87+
std::sort(allEntries.begin(), allEntries.end(), [](const auto& entryA, const auto& entryB) {
88+
return entryA.startPageIdx < entryB.startPageIdx;
89+
});
90+
91+
BlockEntry prevEntry = allEntries[0];
92+
for (common::row_idx_t i = 1; i < allEntries.size(); ++i) {
93+
const auto& entry = allEntries[i];
94+
if (prevEntry.startPageIdx + prevEntry.numPages == entry.startPageIdx) {
95+
prevEntry.numPages += entry.numPages;
96+
} else {
97+
addFreeChunk(prevEntry);
98+
prevEntry = entry;
99+
}
100+
}
101+
addFreeChunk(prevEntry);
102+
}
103+
104+
common::row_idx_t FreeSpaceManager::getNumEntries() const {
105+
common::row_idx_t ret = 0;
106+
for (const auto& freeList : freeLists) {
107+
ret += freeList.size();
108+
}
109+
return ret;
110+
}
111+
112+
static std::pair<common::idx_t, common::row_idx_t> getStartFreeEntry(common::row_idx_t startOffset,
113+
const std::vector<FreeSpaceManager::sorted_free_list_t>& freeLists) {
114+
size_t freeListIdx = 0;
115+
common::row_idx_t curOffset = 0;
116+
while (startOffset - curOffset >= freeLists[freeListIdx].size()) {
117+
KU_ASSERT(freeListIdx < freeLists.size());
118+
curOffset += freeLists[freeListIdx].size();
119+
++freeListIdx;
120+
}
121+
return {freeListIdx, startOffset - curOffset};
122+
}
123+
124+
std::vector<BlockEntry> FreeSpaceManager::getEntries(common::row_idx_t startOffset,
125+
common::row_idx_t endOffset) const {
126+
KU_ASSERT(endOffset >= startOffset);
127+
std::vector<BlockEntry> ret;
128+
auto [freeListIdx, idxInList] = getStartFreeEntry(startOffset, freeLists);
129+
for (; freeListIdx < freeLists.size(); ++freeListIdx) {
130+
auto it = freeLists[freeListIdx].begin();
131+
// TODO(Royi) optimize this
132+
std::advance(it, idxInList);
133+
for (; it != freeLists[freeListIdx].end(); ++it) {
134+
if (ret.size() >= endOffset - startOffset) {
135+
return ret;
136+
}
137+
ret.push_back(*it);
138+
}
139+
idxInList = 0;
140+
}
141+
return ret;
142+
}
143+
144+
} // namespace kuzu::storage

0 commit comments

Comments
 (0)