cmu15445 2023spring project0&1

Project #0 - C++ Primer

资源

课程主页
Bustub Github
在线测试网站 (Entry Code: 2KJRB5), 注意用外国大学以及gmail注册。
lab0资源 我的lab0实现,入门实现有困难的同学可以参考一下。

Backgroud

环境

我的是Ubuntu 9.4.0 + vscode

语法

需要了解的:

c++11: 智能指针、dynamic_cast和const_cast

c++17: string_view、optional

Task #1 - Copy-On-Write Trie

COW Trie的优势就是可以提升并发性能以及降低使并发编程更简易。
对COW Trie的更改会得到一棵新Trie,这使得读操作可以在旧Trie树上进行而不会被阻塞。
但这也会带来另一个问题,即旧节点的管理问题,我们需要回收无效节点,但主动管理比较复杂,实验模板里用了shared_ptr来管理TrieNode。当指向TrieNode的shared_ptr数量为0时,TrieNode会自动释放。

Put

当插入一个key时,从新的根节点到目标节点,都会被创建,区别是前面通过clone,后面通过make_shared。如下图所示,黄色的是复制,红色的是新创建。

在这里插入图片描述

因此,插入大致可分为3步:

  • 从顶向下找出需要复制的节点
  • 从后往前创建新节点
  • 从后往前复制节点

Remove

和Put类似,区别是创建的新节点数为1(或者为0)

有几点需要注意:

  • 边界条件,要能够处理key为空字符串,以及向一棵空树插入数据
  • 用到的指针都是const的,对map的访问需要先find再使用at,不能直接用下标,向map插入数据时需要用const_cast去掉const属性。

Task #2 - Concurrent Key-Value Store

COW使得Trie的并发编程更简单,实验模板里只使用了两个互斥量,而不需要每个node维护互斥量。同时更新操作在获得root后,可以立刻释放root锁,在完成更新操作后,再通过root锁更新root,这样基本上不会阻塞读操作。

Task #3 - Debugging & Task #4 - SQL String Functions

都不难
task3不同环境生成的随机数可能不同,trie_debug_test.cpp的样例可以用以下代码替换

  auto trie = Trie();
  trie = trie.Put<uint32_t>("65", 25);
  trie = trie.Put<uint32_t>("61", 65);
  trie = trie.Put<uint32_t>("82", 84);
  trie = trie.Put<uint32_t>("2", 42);
  trie = trie.Put<uint32_t>("16", 67);
  trie = trie.Put<uint32_t>("94", 53);
  trie = trie.Put<uint32_t>("20", 35);
  trie = trie.Put<uint32_t>("3", 57);
  trie = trie.Put<uint32_t>("93", 30);
  trie = trie.Put<uint32_t>("75", 29);

评测

cmu15445课程主页FAQ里有如何使用评测网站说明,注意要用gmail和外国大学注册。
在这里插入图片描述

Project #1 - Buffer Pool

Task #1 - LRU-K Replacement Policy

本实验的LRU-K和笔者理解的LRU-K略有不同。笔者之前了解的LRU-K是维护两个队列:历史访问队列+LRU队列,在历史访问队列达到k次的数据将缓存到LRU队列中。而此实验的LRU-K更像一个普通的LRU队列,只不过它的替换策略不是根据最近一次访问,而是最近k次访问。

在计算k-distance上,笔者计算的是各个访问时间戳之和,所有节点中时间戳之和最小值就是k-distance最大值,对于访问次数不足k次的,返回一个标志。笔者的GetDistance返回值如下

std::pair<size_t, bool>

需要注意的是,对于不足k次的,返回的是第一次的访问时间 + false

Task #2 - Buffer Pool Manager

注意点:UnpinPage操作中,不能直接将相应page的dirty位设置成is_dirty,因为is_dirty只代表当前线程是否更改了page。

Task #3 - Read/Write Page Guards

注意移动拷贝和赋值需要把other的指针置为空,同时赋值操作需要调用Drop()释放原有资源。
特别注意:加锁和pin的顺序,需要先pin再加锁,以及先解锁再unpin。(笔者被这个bug困扰了很久)
FetchPageBasic这些函数,为了防止代码冗余需要调用FetchPage,又为了防止死锁,笔者写了一个无锁版的FetchPageWithoutLatch供FetchPage和FetchPageBasic等函数调用。

测评

到提交为止(2023-05-10),gradescope大概有100人完成了project1。笔者对于并发的处理都是加锁,并没有优化。

在这里插入图片描述

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
CMU 15445 课程的 Project 0 是一个调试练习,旨在帮助学生熟悉调试工具和技术。在这个项目中,你将开始使用 GDB 和 Valgrind 这两个常用的调试工具,以及一些其他辅助工具。以下是一些问题和步骤,帮助你完成这个练习: 1. 你需要查看项目中提供的代码,并了解它的结构和功能,这样你才能更好地理解程序的逻辑和可能出现的 bug。 2. 接下来,你需要编译项目,并确保没有编译错误。如果出现错误,你需要修复它们,这可能需要检查一些语法错误或缺失的库。 3. 一旦成功编译项目,你就可以使用 GDB 进行调试了。GDB 是一个强大的调试器,可以帮助你找出程序中的错误。你可以使用 GDB 来单步执行代码、设置断点、查看变量的值等等。通过使用 GDB,你可以逐步查看代码运行的路径,并找出程序崩溃或产生错误的原因。 4. 在使用 GDB 进行调试时,你可以通过设置断点来暂停程序的执行,并查看变量的值和程序的状态。你可以使用“break”命令在程序中设置断点,并通过“run”命令启动程序。当程序到达这个断点时,它会停止执行,你可以使用“print”命令查看变量的值,或者“step”命令逐步执行代码。 5. 另一个常用的调试工具是 Valgrind。Valgrind 可以帮助你检测内存泄漏和错误的访问方式。你可以使用“valgrind”命令来运行程序,并查看 Valgrind 的输出。它会告诉你有关程序中任何潜在问题的信息,例如未初始化的变量、访问越界等。 6. 最后,当你发现 bug 并修复它们后,可以运行各种测试用例来验证程序的正确性。测试用例可以帮助你确定程序是否按预期工作,并且在修改代码后,它们可以帮助你确保你的修复没有引入新的错误。 通过完成 CMU 15445 项目 0 的调试练习,你将掌握一些重要的调试技巧和工具,这对于进一步开发和调试软件应用程序将非常有用。希望上述步骤和建议对你有所帮助,祝你顺利完成这个项目!
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值