Skip to content

Commit 400d57c

Browse files
AlSchloxx01cyx
andauthored
feat: add CoW buffer for spring2024 p1 (#690)
* Prepare Spring 24 P1 * fix typo * add default impl for page guard constructor * format * new line at eof --------- Co-authored-by: xx01cyx <caoyuanxin0531@outlook.com>
1 parent 258d8e2 commit 400d57c

File tree

6 files changed

+174
-17
lines changed

6 files changed

+174
-17
lines changed

src/include/buffer/buffer_pool_manager.h

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "buffer/lru_k_replacer.h"
2121
#include "common/config.h"
2222
#include "recovery/log_manager.h"
23+
#include "storage/disk/cow_buffer.h"
2324
#include "storage/disk/disk_scheduler.h"
2425
#include "storage/page/page.h"
2526
#include "storage/page/page_guard.h"
@@ -192,6 +193,8 @@ class BufferPoolManager {
192193
std::list<frame_id_t> free_list_;
193194
/** This latch protects shared data structures. We recommend updating this comment to describe what it protects. */
194195
std::mutex latch_;
196+
/** This buffer is for the leaderboard task. You may want to use it to optimize the write requests. */
197+
CoWBuffer cow_buffer_ __attribute__((__unused__));
195198

196199
/**
197200
* @brief Allocate a page on disk. Caller should acquire the latch before calling this function.

src/include/storage/disk/cow_buffer.h

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// BusTub
4+
//
5+
// cow_buffer.h
6+
//
7+
// Identification: src/include/storage/disk/cow_buffer.h
8+
//
9+
// Copyright (c) 2015-2023, Carnegie Mellon University Database Group
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#pragma once
14+
15+
#include <cstdint>
16+
#include <cstring>
17+
#include "common/config.h"
18+
#include "common/macros.h"
19+
#include "storage/page/page.h"
20+
21+
namespace bustub {
22+
23+
/**
24+
* CoWBuffer provides extra memory space other than the buffer pool to store the copy-on-write pages.
25+
* It's purpose is to gather the copy of pages that are about to be written back to disk, so that the bpm
26+
* doesn't have to incur IO penality and wait for the write to be completed when evicting.
27+
* Spring 24: The buffer is limited to store a constant number of pages in total (8).
28+
* !! ANY ATTEMPTS TO ADD ANOTHER IN-MEMORY CACHE WILL BE REVIEWED MANUALLY AS PER LEADERBOARD POLICY !!
29+
*/
30+
class CoWBuffer {
31+
public:
32+
CoWBuffer() : cow_pages_{new Page[8]} {}
33+
~CoWBuffer() { delete[] cow_pages_; }
34+
DISALLOW_COPY_AND_MOVE(CoWBuffer);
35+
36+
/**
37+
* @brief Adds a new page to the CoW buffer.
38+
* @param page the page pointer from the bpm that is about to be evicted.
39+
* @return pointer to the copied page in the buffer, or nullptr if the buffer is full.
40+
*/
41+
auto Add(Page *page) -> Page * {
42+
if ((page == nullptr) || IsFull()) {
43+
return nullptr;
44+
}
45+
46+
uint32_t slot = FindFreeSlot();
47+
memcpy(cow_pages_[slot].GetData(), page->GetData(), BUSTUB_PAGE_SIZE);
48+
MarkSlotUsed(slot);
49+
50+
return cow_pages_ + slot;
51+
}
52+
53+
/**
54+
* @brief Removes a page from the CoW buffer.
55+
* @param page the pointer previously returned by Add.
56+
*/
57+
auto Remove(Page *page) -> void {
58+
if (page != nullptr) {
59+
MarkSlotFree(page - cow_pages_);
60+
}
61+
}
62+
63+
private:
64+
/** @brief Whether the buffer is full. */
65+
auto IsFull() -> bool { return free_slot_bitmap_ == 0xFFU; }
66+
67+
/** @brief Finds a free slot in the buffer, if not full. */
68+
auto FindFreeSlot() -> uint32_t {
69+
BUSTUB_ASSERT(!IsFull(), "no free slot in cow buffer");
70+
uint32_t i = 0;
71+
uint8_t bitmap = free_slot_bitmap_;
72+
while ((bitmap & 1U) != 0) {
73+
bitmap >>= 1;
74+
i++;
75+
}
76+
return i;
77+
}
78+
79+
/** @brief Marks a free slot as used. */
80+
void MarkSlotUsed(uint32_t slot) {
81+
BUSTUB_ASSERT(((free_slot_bitmap_ >> slot) & 1U) == 0, "slot has already been used");
82+
free_slot_bitmap_ |= (1U << slot);
83+
}
84+
85+
/** @brief Marks a used slot as free. */
86+
void MarkSlotFree(uint32_t slot) {
87+
BUSTUB_ASSERT(((free_slot_bitmap_ >> slot) & 1U) == 1, "slot is already free");
88+
free_slot_bitmap_ &= ~(1U << slot);
89+
}
90+
91+
/** The array of CoW buffer pages. */
92+
Page *cow_pages_;
93+
/** The bitmap that records which slots are free. */
94+
uint8_t free_slot_bitmap_{0};
95+
};
96+
97+
} // namespace bustub

src/include/storage/page/page_guard.h

+15-15
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class BasicPageGuard {
1717
BasicPageGuard(const BasicPageGuard &) = delete;
1818
auto operator=(const BasicPageGuard &) -> BasicPageGuard & = delete;
1919

20-
/** TODO(P2): Add implementation
20+
/** TODO(P1): Add implementation
2121
*
2222
* @brief Move constructor for BasicPageGuard
2323
*
@@ -29,7 +29,7 @@ class BasicPageGuard {
2929
*/
3030
BasicPageGuard(BasicPageGuard &&that) noexcept;
3131

32-
/** TODO(P2): Add implementation
32+
/** TODO(P1): Add implementation
3333
*
3434
* @brief Drop a page guard
3535
*
@@ -40,7 +40,7 @@ class BasicPageGuard {
4040
*/
4141
void Drop();
4242

43-
/** TODO(P2): Add implementation
43+
/** TODO(P1): Add implementation
4444
*
4545
* @brief Move assignment for BasicPageGuard
4646
*
@@ -61,7 +61,7 @@ class BasicPageGuard {
6161
*/
6262
~BasicPageGuard();
6363

64-
/** TODO(P2): Add implementation
64+
/** TODO(P1): Add implementation
6565
*
6666
* @brief Upgrade a BasicPageGuard to a ReadPageGuard
6767
*
@@ -72,7 +72,7 @@ class BasicPageGuard {
7272
*/
7373
auto UpgradeRead() -> ReadPageGuard;
7474

75-
/** TODO(P2): Add implementation
75+
/** TODO(P1): Add implementation
7676
*
7777
* @brief Upgrade a BasicPageGuard to a WritePageGuard
7878
*
@@ -114,11 +114,11 @@ class BasicPageGuard {
114114
class ReadPageGuard {
115115
public:
116116
ReadPageGuard() = default;
117-
ReadPageGuard(BufferPoolManager *bpm, Page *page) : guard_(bpm, page) {}
117+
ReadPageGuard(BufferPoolManager *bpm, Page *page);
118118
ReadPageGuard(const ReadPageGuard &) = delete;
119119
auto operator=(const ReadPageGuard &) -> ReadPageGuard & = delete;
120120

121-
/** TODO(P2): Add implementation
121+
/** TODO(P1): Add implementation
122122
*
123123
* @brief Move constructor for ReadPageGuard
124124
*
@@ -128,7 +128,7 @@ class ReadPageGuard {
128128
*/
129129
ReadPageGuard(ReadPageGuard &&that) noexcept;
130130

131-
/** TODO(P2): Add implementation
131+
/** TODO(P1): Add implementation
132132
*
133133
* @brief Move assignment for ReadPageGuard
134134
*
@@ -137,7 +137,7 @@ class ReadPageGuard {
137137
*/
138138
auto operator=(ReadPageGuard &&that) noexcept -> ReadPageGuard &;
139139

140-
/** TODO(P2): Add implementation
140+
/** TODO(P1): Add implementation
141141
*
142142
* @brief Drop a ReadPageGuard
143143
*
@@ -148,7 +148,7 @@ class ReadPageGuard {
148148
*/
149149
void Drop();
150150

151-
/** TODO(P2): Add implementation
151+
/** TODO(P1): Add implementation
152152
*
153153
* @brief Destructor for ReadPageGuard
154154
*
@@ -174,11 +174,11 @@ class ReadPageGuard {
174174
class WritePageGuard {
175175
public:
176176
WritePageGuard() = default;
177-
WritePageGuard(BufferPoolManager *bpm, Page *page) : guard_(bpm, page) {}
177+
WritePageGuard(BufferPoolManager *bpm, Page *page);
178178
WritePageGuard(const WritePageGuard &) = delete;
179179
auto operator=(const WritePageGuard &) -> WritePageGuard & = delete;
180180

181-
/** TODO(P2): Add implementation
181+
/** TODO(P1): Add implementation
182182
*
183183
* @brief Move constructor for WritePageGuard
184184
*
@@ -188,7 +188,7 @@ class WritePageGuard {
188188
*/
189189
WritePageGuard(WritePageGuard &&that) noexcept;
190190

191-
/** TODO(P2): Add implementation
191+
/** TODO(P1): Add implementation
192192
*
193193
* @brief Move assignment for WritePageGuard
194194
*
@@ -197,7 +197,7 @@ class WritePageGuard {
197197
*/
198198
auto operator=(WritePageGuard &&that) noexcept -> WritePageGuard &;
199199

200-
/** TODO(P2): Add implementation
200+
/** TODO(P1): Add implementation
201201
*
202202
* @brief Drop a WritePageGuard
203203
*
@@ -208,7 +208,7 @@ class WritePageGuard {
208208
*/
209209
void Drop();
210210

211-
/** TODO(P2): Add implementation
211+
/** TODO(P1): Add implementation
212212
*
213213
* @brief Destructor for WritePageGuard
214214
*

src/storage/page/page_guard.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ auto BasicPageGuard::operator=(BasicPageGuard &&that) noexcept -> BasicPageGuard
1111

1212
BasicPageGuard::~BasicPageGuard(){}; // NOLINT
1313

14+
ReadPageGuard::ReadPageGuard(BufferPoolManager *bpm, Page *page) {}
15+
1416
ReadPageGuard::ReadPageGuard(ReadPageGuard &&that) noexcept = default;
1517

1618
auto ReadPageGuard::operator=(ReadPageGuard &&that) noexcept -> ReadPageGuard & { return *this; }
@@ -19,6 +21,8 @@ void ReadPageGuard::Drop() {}
1921

2022
ReadPageGuard::~ReadPageGuard() {} // NOLINT
2123

24+
WritePageGuard::WritePageGuard(BufferPoolManager *bpm, Page *page) {}
25+
2226
WritePageGuard::WritePageGuard(WritePageGuard &&that) noexcept = default;
2327

2428
auto WritePageGuard::operator=(WritePageGuard &&that) noexcept -> WritePageGuard & { return *this; }

src/storage/table/table_heap.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ auto TableHeap::InsertTuple(const TupleMeta &meta, const Tuple &tuple, LockManag
6262

6363
page_guard.Drop();
6464

65-
// acquire latch here as TSAN complains. Given we only have one insertion thread, this is fine.
66-
npg->WLatch();
6765
auto next_page_guard = WritePageGuard{bpm_, npg};
6866

6967
last_page_id_ = next_page_id;

test/storage/cow_buffer_test.cpp

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// BusTub
4+
//
5+
// page_guard_test.cpp
6+
//
7+
// Identification: test/storage/page_guard_test.cpp
8+
//
9+
// Copyright (c) 2015-2023, Carnegie Mellon University Database Group
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "storage/disk/cow_buffer.h"
14+
#include "gtest/gtest.h"
15+
16+
namespace bustub {
17+
18+
// NOLINTNEXTLINE
19+
TEST(CoWBufferTest, ScheduleWriteReadPageTest) {
20+
CoWBuffer cow{};
21+
std::vector<Page *> cow_pages{};
22+
for (size_t i{0}; i < 8; i++) {
23+
Page bpm_page{};
24+
auto content{"Meuh!: " + std::to_string(i) + " 🐄🐄🐄🐄"};
25+
std::strncpy(bpm_page.GetData(), content.data(), BUSTUB_PAGE_SIZE);
26+
27+
Page *cow_page{cow.Add(&bpm_page)};
28+
EXPECT_NE(cow_page, nullptr);
29+
EXPECT_NE(cow_page, &bpm_page);
30+
cow_pages.push_back(cow_page);
31+
EXPECT_EQ(std::memcmp(cow_page->GetData(), bpm_page.GetData(), BUSTUB_PAGE_SIZE), 0);
32+
}
33+
34+
Page extra_page{};
35+
Page *cow_extra_page{cow.Add(&extra_page)};
36+
EXPECT_EQ(cow_extra_page, nullptr);
37+
38+
for (size_t i{0}; i < 8; i++) {
39+
auto check{"Meuh!: " + std::to_string(i) + " 🐄🐄🐄🐄"};
40+
EXPECT_EQ(std::memcmp(cow_pages[i]->GetData(), check.data(), check.size()), 0);
41+
}
42+
43+
cow.Remove(cow_pages[5]);
44+
Page replace_page{};
45+
cow_pages[5] = cow.Add(&replace_page);
46+
EXPECT_NE(cow_pages[5], nullptr);
47+
48+
for (size_t i{0}; i < 8; i++) {
49+
cow.Remove(cow_pages[i]); // Shouldn't crash.
50+
}
51+
52+
cow.Remove(nullptr); // Shouldn't crash.
53+
}
54+
55+
} // namespace bustub

0 commit comments

Comments
 (0)