CMU 15-445 Project 2 B+ Tree 技术总结

2024-05-08 14:18
文章标签 技术 总结 15 tree 445 project cmu

本文主要是介绍CMU 15-445 Project 2 B+ Tree 技术总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先是教科书的伪代码

感觉基本都要按这个来写,不然(可能)写不出来的,PPT 的讲解也不够详细。Project 的代码结构的确是根据这个伪代码来设计的。不过我感觉这种写法的伪代码有点难看。

并发的要点

基本做 lab 的过程就是全程对着上面两个 guideline 来操作。

然后是我的一些技术总结踩坑的地方(可能会没有帮助 )

  1. 锁的顺序,死锁。
  2. 实现的方法是某些方法保证进来之前获取锁,谁用到锁谁释放。
  3. 学习了怎么用条件编译实现无虚函数继承的多态方法。
  4. 边界条件不知道怎么解决只想到用 bool 来暴力编程。
  5. 异常的问题,理论上不 unique 的应该正常返回,which 要求 release 所有的锁。

all tests passed 之后总结一下

首先是浪费时间 accidental 的事情

  • 这里感谢 discord 的一个主管人的解释以及discord上各位前人的探索,autograder fail 的情况可能是超时或者内存,部分前人提到与不兼容的C++17有关,我于是解决了第一个 auto [] 解包的不兼容,但是这里我的思考不够坚定,我看到了有人用 constexpr + NOLINT(这个 NOLINT 也感谢 discord 的小伙伴)还点赞的(说明他通过了),但是我还是纠结在 constexpr 导致花了一些时间去掉 constexpr 和尝试其他的语句会不会有不兼容,但是实际我也看到了有的人提到了 log 的问题,但是一开始我还是没有尝试 upload 我的 logger,最后还是靠  upload logger 来解决的。。还要感谢一个人说的从 base code 开始一点添加和修改直到不能通过(二分法)。我的思考方法还得提升 since 之前我 buffer pool 里面是改了 bug 才通过的,由此应该能够得到结论是 bufferpool 超时(deadlock) 了,和标准和 assert include 头无关。而 local test 的时候也的确发现了打 log 花的时间是几千倍。
  • 第一个是我没有去掉 log 然后就 上传,导致运行不了(花时间太长了)。
  • 而 autograder 的 logger 又是没用的, 他用 constexpr 定义了 logger 这个无法生效,需要改成 define 的,然后上传的时候避免超时必须禁用所有(默认开 debug 和 trace)。

第二个是一些错误点(解决 logger 问题后从35分到满分只花不到一个小时而且有 25 名在一堆没用 constexpr if 的情况下)

  • 我一开始因为有的地方(比如 root page)需要 acquire 到 latch 之后马上 enqueue,所以我把这两布写在一起了。然而实际是你 acquire 到 child  的 latch 之后要判断 ancestor 的之后保留自己的。所以必须先保存这个 latch,等判断玩 ancestor 的之后再 出que。
  • Unpin 的问题,我做的时候写了一个 PageResource RAII 类来进行 Page 的 unpin ,dirty 写回,删除等管理。当时出了问题。Debug 的时候我一开始把所有的 PageResource 都去掉了,导致 delete 的时候可能会有一些 pined 值大于等于 1 的情况,这个很明显是我搞错了。
  • 在 FindLeafPage 里面 enque 那些是不用 unpin 的,因为他们的 unpin 会延迟进行,但是其他函数比如 CoalesceOrRedistibute 和 Redistibute 都会 fetch parent 或者中间的 sibling,其实 sibling 可以加入到 queue 里也可以不加(因为有 parent 被锁了,安全),这些 pin 是没有对应的 unpin 的所以 RAII PageResource 还是要到位的。
  • 但是奇怪的是好像我拿满分的那个版本的确会去掉了部分 PageResource,我还认为这是正确方案。
  • 用了 constexpr if 的结果反而没有加快,(4.33->4.34),这一点不管了,不过可能是我 get value 改用了 统一返回 tuple 的 FindLeafPagePro 进行了更多数据相关引发的平衡了 constexpr 的效果?或者编译器本来就把我那些非 constexpr if 优化成 constexpr if。这个的确难讲)。
  • 然后还有一个多谢博客园大佬,于是我改了 FindLeafPage 的 return value 。主要是他对一个问题的分析:

我发现,在执行删除操作之间,有另一个线程,对需要被删除的叶子节点进行了Fetch、加锁、解锁,但并没有unpin

也就是说,问题的根源在于,解锁与Unpin不是原子的

要想解决这个问题,方案有两个:

  1. 让解锁与Unpin变成原子的。这需要引入一把新的锁。
  2. 在删除时,若发现Pin Count不为0,则sleep一段时间。等待另一个线程unpin。若苏醒后发现Pin Count仍不为0,则不断循环。

方案2比较简单,因此我选择了方案2。在这之后,问题迎刃而解,测试通过。

From <CMU15-445 Lab2 B+Tree全记录 - sun-lingyu - 博客园>

为了适合多核的情况,我没有修改 BufferPool (since 这个本来就不是 buffer pool 的问题),只需要在 remove 的函数里 delete 失败(根据 bufferpool 的API规定 pined 值大于 0 必须 return false 而不进行 deletee)的话就 spin 一定的次数,直到 delete 成功。这个能 work 是因为 spin 的话,多核如果执行到了 unpin 那么下一个循环过程马上就能 delete 成功。

其他总结

  • 这次总的时间花了挺长的,到现在也差不多半个月了,buffer pool 之后国庆节基本没动手,到 10 月 8日才开始做这个,checkpoint 1 通过是 10 月 12 日,之后一致拖到今天(10月22日)才全部完成。历时 16 天,checkpoint 2 历时 10 天,虽然其中学 Cmake 花了一些时间。总的来说压力还是不够大,接下来事情很多得加大压力才行了。同时马上开始了 query 的 project 3。
  • C++ 也重温了不少东西,一个是一开始我感觉那个不用虚函数的多态太坑了,当时是必要的不然虚函数表就能把on disk数据结构打乱,但是其实 C++ 也提供了 元编程的工具编译器生成代码,is_same_v 和 constexpr if 还是不错的,用上之后 runtime 应该没有这么多 if else 负担。所以通过 template 来实现多态(duck typing)其实也是一个方案。
  • 然后是 tuple,实际编程才体会到多返回值是有用的,虽然用参数也不是不行。之后多习惯 auto 解包吧(虽然445 编程不能用 auto 解包)。
  • RAII 的确很好,但是这里他的框框太多了,我有点难说怎么实现一个好的 PageResource 反而让这个 RAII 类很臃肿,所以我实际是简单问题复杂化了,实际如果可以手动管理所有的情况,上RAII包装类可能复杂化了,或者说我提供的选项不够多?除非修改他原有的函数的传参的方法。不过最终起码还是解决我忘记 unpin 的问题的,之后继续学习熟悉。
  • const 的理解,一定要用 clang-tidy 等静态分析工具,这次加深了 code review 的重要。我一开始以为 const & 是可以修改他本身的,但是我错了。必须加深这个记忆,即 const 和 非 const 函数也是搬过来的。总之就一定是修改对象本身的时候用 pointer,读取对象的话才用 const & 而永远不要单独使用 &。(虽然 effective c++ 看到过,但是果然不实践的话是记不住的)。
  • 对多线程的东西更加魔怔了,首先断点的不能用的,必须要打 log,因此也养成写 LOG 的好习惯,一开始我的 log 乱写,导致看起来很难受。然后是 thread id,这里我没办法(而且他的 logger 本身好像并不是线程安全的,有一些乱序,之后研究一下异步 logger 怎么写吧)。
  • 然后就是测试的 down scale 和找特征,不一定要很大规模才能复现,这里 B+ tree 的话主要就是和 page 支持的 size 和 插入的数量有关,多用二分法。
  • 数据结构体这个 debug 思路真的值得学习。
  • 对于原子性更加理解,就是博客园大佬说的那个情况,这个分析和情况都很值得记住。
  • 思考过程就是说怎么排除问题怎么分析问题,这个在 accidental 分析里说了,之后还要持续优化提高才行。
  • B+ tree 本身的话,我一开始没有理解为什么判断的时候用的条件不一样,我感觉 project 设计得的确有些不妥。这一种 B+ tree 本身的规定是
    • 每个结点至多有m 个键;
    • 除根结点外,每个结点至少有[m/2]个子女,根结点至少有两个子女;

如果定义的时候用的是至多 m 个子女又另说了。所以这部分很歧义(主要是他计算一个 4096 bytes 的 page 内的数量又没有留空一个来 move)。

这篇关于CMU 15-445 Project 2 B+ Tree 技术总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/970548

相关文章

Python中连接不同数据库的方法总结

《Python中连接不同数据库的方法总结》在数据驱动的现代应用开发中,Python凭借其丰富的库和强大的生态系统,成为连接各种数据库的理想编程语言,下面我们就来看看如何使用Python实现连接常用的几... 目录一、连接mysql数据库二、连接PostgreSQL数据库三、连接SQLite数据库四、连接Mo

Git提交代码详细流程及问题总结

《Git提交代码详细流程及问题总结》:本文主要介绍Git的三大分区,分别是工作区、暂存区和版本库,并详细描述了提交、推送、拉取代码和合并分支的流程,文中通过代码介绍的非常详解,需要的朋友可以参考下... 目录1.git 三大分区2.Git提交、推送、拉取代码、合并分支详细流程3.问题总结4.git push

Kubernetes常用命令大全近期总结

《Kubernetes常用命令大全近期总结》Kubernetes是用于大规模部署和管理这些容器的开源软件-在希腊语中,这个词还有“舵手”或“飞行员”的意思,使用Kubernetes(有时被称为“... 目录前言Kubernetes 的工作原理为什么要使用 Kubernetes?Kubernetes常用命令总

Python中实现进度条的多种方法总结

《Python中实现进度条的多种方法总结》在Python编程中,进度条是一个非常有用的功能,它能让用户直观地了解任务的进度,提升用户体验,本文将介绍几种在Python中实现进度条的常用方法,并通过代码... 目录一、简单的打印方式二、使用tqdm库三、使用alive-progress库四、使用progres

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

Java向kettle8.0传递参数的方式总结

《Java向kettle8.0传递参数的方式总结》介绍了如何在Kettle中传递参数到转换和作业中,包括设置全局properties、使用TransMeta和JobMeta的parameterValu... 目录1.传递参数到转换中2.传递参数到作业中总结1.传递参数到转换中1.1. 通过设置Trans的

C# Task Cancellation使用总结

《C#TaskCancellation使用总结》本文主要介绍了在使用CancellationTokenSource取消任务时的行为,以及如何使用Task的ContinueWith方法来处理任务的延... 目录C# Task Cancellation总结1、调用cancellationTokenSource.

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用