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

相关文章

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

Python实现图片分割的多种方法总结

《Python实现图片分割的多种方法总结》图片分割是图像处理中的一个重要任务,它的目标是将图像划分为多个区域或者对象,本文为大家整理了一些常用的分割方法,大家可以根据需求自行选择... 目录1. 基于传统图像处理的分割方法(1) 使用固定阈值分割图片(2) 自适应阈值分割(3) 使用图像边缘检测分割(4)

Windows Docker端口占用错误及解决方案总结

《WindowsDocker端口占用错误及解决方案总结》在Windows环境下使用Docker容器时,端口占用错误是开发和运维中常见且棘手的问题,本文将深入剖析该问题的成因,介绍如何通过查看端口分配... 目录引言Windows docker 端口占用错误及解决方案汇总端口冲突形成原因解析诊断当前端口情况解

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

java常见报错及解决方案总结

《java常见报错及解决方案总结》:本文主要介绍Java编程中常见错误类型及示例,包括语法错误、空指针异常、数组下标越界、类型转换异常、文件未找到异常、除以零异常、非法线程操作异常、方法未定义异常... 目录1. 语法错误 (Syntax Errors)示例 1:解决方案:2. 空指针异常 (NullPoi

Java反转字符串的五种方法总结

《Java反转字符串的五种方法总结》:本文主要介绍五种在Java中反转字符串的方法,包括使用StringBuilder的reverse()方法、字符数组、自定义StringBuilder方法、直接... 目录前言方法一:使用StringBuilder的reverse()方法方法二:使用字符数组方法三:使用自

Python依赖库的几种离线安装方法总结

《Python依赖库的几种离线安装方法总结》:本文主要介绍如何在Python中使用pip工具进行依赖库的安装和管理,包括如何导出和导入依赖包列表、如何下载和安装单个或多个库包及其依赖,以及如何指定... 目录前言一、如何copy一个python环境二、如何下载一个包及其依赖并安装三、如何导出requirem

Rust格式化输出方式总结

《Rust格式化输出方式总结》Rust提供了强大的格式化输出功能,通过std::fmt模块和相关的宏来实现,主要的输出宏包括println!和format!,它们支持多种格式化占位符,如{}、{:?}... 目录Rust格式化输出方式基本的格式化输出格式化占位符Format 特性总结Rust格式化输出方式