前言
发现前几年做的15445内容忘得差不多了,最近再来搓一遍玩玩。
由于教授要求非public,本博客将不会出现完整代码,顶多出现代码片段。
项目依旧基于C++17构建,现在看来当年算新鲜的标准已经有点落后了()
相关链接
官方网站
之前做23Spring的环境配置
FAQ(发现这个版本还专门提了池先森。。)
相关环境
配置:14700K + 64G
IDE:Visual studio 2026 insiders
主机环境:Windows11
Linux环境:WSL2 + Ubuntu24.04
0. 环境配置
本次选用Visual Studio配合WSL2搭建环境,关于VS,安装这个东西:

Github仓库
然后拉仓库,参考官方链接:
首先创建一个私有库

记下来URL:

WSL配置
在WSL里创一个文件夹,然后拉公共仓库并镜像到自己的仓库:
git clone --bare https://github.com/cmu-db/bustub.git bustub-public
cd bustub-public
git push git@github.com:JMC2002/15445_25Fall.git master # 记得换成你自己的!

删除本地克隆,拉取自己的,这里建议用https,不然很慢
cd ..
rm -rf bustub-public
git clone https://github.com/JMC2002/15445_25Fall.git # 记得换成你自己的!

将公共仓库添加为第二个远程用来更新:
git remote add public https://github.com/cmu-db/bustub.git
git remote -v

拉取公共仓库的更改,这样更新:
git pull public master
由于是私有仓库,我们禁用Action,免得用完配额

然后我们运行脚本安装必要的包:
sudo build_support/packages.sh

构建:
mkdir build
cd build
cmake ..
make
这里提一下,这个年份默认是用的clang15,如果你想要别的版本的话就同时改一下安装脚本和cmake:


另外,这里可能会跳一个警告,说你的编译器用的不是clang-15怎么怎么样,就手动指定一下编译器再make:
cmake .. -DCMAKE_C_COMPILER=clang-15 -DCMAKE_CXX_COMPILER=clang++-15
make
此外,make的时候默认是单线程,很慢,可以这样线程拉满编译:
rm -rf build
mkdir build && cd build
cmake .. -DCMAKE_C_COMPILER=clang-15 -DCMAKE_CXX_COMPILER=clang++-15
make -j$(nproc)
单线程的时候用了两分多钟

这里开了28线程半分钟就搞好了:

本地测试:
make check-tests

由于还没开始写,会报错,这是正常的,后面我们就用这个测试。
Visual Studio
VS直接打开WSL的项目的话会卡卡的,不好用,这里我们在Windows里再拉一份,刚刚我们做好了自己的仓库,这里直接克隆下来:

接下来,我们在Windows上用VS打开,选择打开文件夹,比如我这是\\wsl.localhost\Ubuntu\home\jmc\15445_25fall\15445_25Fall;
在打开后,可能啥也看不见,这个时候我们在解决方案资源管理器里选择文件夹视图:

这时候可能会扫描到我们的CMake然后弹配置,如果没弹的话就找到本地文件夹,然后删掉.vs后再打开这个文件夹试试

选择我们需要的WSL主机:

然后在设置里选择始终使用CMake预设

这里会默认给一个Linux-Debug,我们点这个管理配置

插入这样一段,其中,debug模式下开启了"BUSTUB_SANITIZER": "address,leak":
{
"name": "wsl-clang-debug",
"displayName": "WSL: Ubuntu (Clang Debug)",
"description": "在WSL上使用Clang15",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"BUSTUB_SANITIZER": "address,leak",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
"CMAKE_C_COMPILER": "clang-15",
"CMAKE_CXX_COMPILER": "clang++-15"
},
{
"name": "wsl-clang-release",
"displayName": "WSL: Ubuntu (Clang Release)",
"description": "在WSL上使用Clang15",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
"CMAKE_C_COMPILER": "clang-15",
"CMAKE_CXX_COMPILER": "clang++-15"
},
"environment": {
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"vendor": { "microsoft.com/VisualStudioRemoteSettings/CMake/2.0": { "remoteSourceRootDir": "$env{HOME}/.vs/$ms{projectDirName}" } }
},

随便选一个启动项,比如bustub-shell:

运行,然后发现跳了个异常,因为我们没实现,说明环境配置大功告成,可喜可贺!

这时候我们发现git不可信任:

我们右键信任即可。
然后就可以做第一个提交了:

然后在Git>管理远程仓库中添加一个public用来拉取公共库的更新,提取写https://github.com/cmu-db/bustub.git,推送也可以写这个(反正也推不上去,不用管):

或者也可以Ctrl+ˋ打开命令行,然后直接执行前面那几个命令拉取:

注册账号
去gradescope注册个号,点击右上角Sign Up
Student

课程号填5R4XPZ(2025Fall),学校填Carnegie Mellon University,填好其他信息及邮件,会给你的邮箱发送一封设置密码的邮件,密码要求最短12位。这个验证码是专门提供给非CMU学生的,来自FAQ
弄好后是这样的,可以提交:

1. Project 0
OK,终于可以开始做Project0了,这里我会详细演示一下工作流,以及简单点评一下这个Project。
签出分支
我们做每一个project前,先签出一个分支,做完后再合并回master(因为后一个project往往强依赖于前一个),这样避免把代码搞乱


作业背景
这次的p0与前几次大相径庭,是做一个并发数据结构
先给了你一个背景,假设你是一个网站管理员,现在你想找哪个IP的访问量最大,最直接的想法是用一个哈希表(ip, cnt)去存每个IP对应的访问次数,但这在数据量很大的时候占用的内存是非常高的,于是引入了CM Sketch这个东西。
这个是一种概率数据结构,简单来说,前面哈希表存的key太多,那我直接不存key本身,改为直接维护哈希值的计数,这样就省去了很大的内存开销,但是很显然,哈希函数是有冲突的,因此我们可以在每一个IP插入的时候,做depth次哈希,给每次哈希算出来的哈希值加一个计数,在要查询的时候再次做depth次哈希,找到里面的最小值,认为这个最小值就是我们IP的计数,这样尽管严格来说还是不完全准确,但可以得到一个相对准确的计数值。
内存上,假设我们把哈希值(比如取模之类的、相当于再做一次哈希)约束到width内,我们只需要维护一个w*d的二维数组,就减少了很多内存。
Task
然后看一下任务,我们要实现的就是一个构造函数(带参+移动构造)、一个移动赋值、插入、查询、清除、合并、查询TopK

有几点注意事项是要使用他提供的哈希函数、在做插入时要线程安全,同时还要杜绝一把大锁锁完的情况,锁尽量细粒度或者无锁

文件
我们要写的就是这个count_min_sketch.h/count_min_sketch.cpp

一些细节
- 我们可以存一个
std::unique_ptr<std::atomic<uint32_t>[]> table_;,就可以避免使用锁了 - 求
Top-k的时候,可以使用std::nth_element+std::sort,先做快选再排k,这样的时间复杂度是 O ( n + k log k ) O(n+k\log k) O(n+klogk),类似的可以用std::partial_sort,这样的时间复杂度是 O ( n log k ) O(n\log k) O(nlogk),考虑到 k k k一般比较小,还是选前者比较好 - 原始的哈希函数产生器是捕获的
this指针,在做移动的时候,如果直接std::move哈希函数数组就会导致垂悬引用,因此我们在捕获的时候改为值捕获width即可
提交代码
写完代码后,我们选择count_min_sketch_test

如果没问题的话,在远程控制台就能看到测试通过的输出以及跑分

这个时候正常就该跑一下clang-tidy的检查了
make format
make check-clang-tidy-p0
我们打开终端、选择WSL,正常应该启动就在这个目录,或者在外面开一个终端也行:

打开~/.vs/15445_25Fall/out/build(这个路径对应之前填的地方)

然后打开对应的文件,进去就可以看到cmake生成的文件了

由于我们之前用的生成器是ninja,因此这里执行:
ninja format
ninja check-clang-tidy-p0
最后不报错就说明检查通过:

关于内存泄漏检测,如果你使用前面我提供的设置,换用Debug跑一下能过就行。
另外,值得注意的一点是,提交要求UTF-8,右下角这里如果不是UTF-8的话需要手动保存成UTF-8(无签名)

此外,不知道为什么直接本地format不会应用头文件顺序的检查,因此最好手动把头文件按字母顺序排列一下,免得提交上去格式检查不通过。
一切无误后,生成一个提交
ninja submit-p0
如果是全新的环境,可能需要安装zip:
sudo apt update
sudo apt install zip
然后可以看见生成了一个zip到(生成的)项目根目录下

然后提交:

提交上去后会自动给你打分

等一会后就会显示是否通过/哪个地方没通过:

合并分支
通过后就可以合并分支了,先推送更改:

然后切换到master,再右键p0,合并到当前分支:

顺便还能打一个tag:

3. 结语
本文也算开了个头,演示了一下这个东西的基本工作流程,后面应该就不会把工作流写这么细了。
1897

被折叠的 条评论
为什么被折叠?



