从地址空间到执行引擎:GPGPU 实验学习笔记¶
主要贡献者
- 作者:@jensenojs
背景介绍¶
在校期间做过南大 PA(本质上是造一个 mini qemu),现在工作了两三年的数据库内核(别来),所以大部分概念不是第一次遇到,代码难度也不大。特别是有了 ai 大人的帮助,实际上相当轻松。但工作挤占精力太多,没太多时间投入这个项目。这个博客也是 14 号凌晨写的,不知道能不能 merge 进去,微信群找的几个工作几年的网友可能也都咕咕咕了。迟点还得打探一下。
这次选 GPGPU 方向也是想以后往 ai infra 或者 riscv 靠一靠。学习方式主要是 vibe coding 和 vibe study,测试跑在自己 fedora 服务器上,ai 过测的速度快到让人没有实感,和以前手搓 15-445 或者 PA 完全不是一个体验。毕竟没亲手写多少 bug,时间更多花在让 ai 带我回忆和深化概念和逻辑链路上。一边觉得站在旧训练上这算偷懒,一边又不确定以后工作里遇到这类 bug 还能不能这么轻松——不过我目前感觉,相对熟悉的领域应该还好。
专业阶段¶
三篇笔记的写作顺序就是 GPGPU 一层一层接入 QEMU 的顺序,每篇解决一个问题,留一个问题给下一篇。
做 PA 的时候地址空间就是 RAM,写 0x80000000 就是写内存,没什么好想的。到了 QEMU 里 GPGPU 注册了 BAR0,CPU 写这个地址,没有内存被写——落到的是 gpgpu_ctrl_read(),一个函数调用。地址空间本质上是一张路由表,MemoryRegion 树定义的是访存往哪走,不是存储怎么分。PCIe 让外设可枚举,但这件事有前提:FDT 得先把 PCIe 入口的位置告诉 kernel,kernel 才有能力去 config space 里读 Vendor ID、探测 BAR、分配窗口。设备挂上地址地图了。CPU reset 之后从哪开始执行、怎么知道地图上有 GPGPU——这个问题留着。
上一篇把设备接进了地址空间,guest 怎么发现它还没有答案。答案从 host 侧的准备开始——把 OpenSBI、kernel 镜像、DTB blob 放进 guest 地址空间。放进去只是字节登记,vCPU reset 之后才真正取指。MROM 跳板读 fw_dynamic_info 跳到 OpenSBI,OpenSBI 跳到 kernel。kernel 解析 DTB 发现 PCIe root complex——于是回到上一篇,开始枚举 endpoint。host 准备材料是 prepare,vCPU 取指执行是 execute,分界和数据库里一样清晰。设备被发现了,但前 12 个测试里它也只做被动响应:CPU 写寄存器,handler 改字段,读回来验证。自己的计算能力还没启动。
前两篇解决的是设备怎么被看见。kernel-exec 开始,设备要自己干活了——从 VRAM 取 RISC-V 指令,按 warp 组织线程,执行完写回结果。这个跳跃引出一个更底层的问题:GPU 的执行模型为什么长这样。
标量循环的管理开销是表达方式造成的,不是计算本身的属性。向量处理器用时间并行消掉它,SIMD 靠空间并行几乎免费地加倍吞吐——但两者都不给处理单元编号,碰到条件分支和非连续访问只能在软件层补偿。SIMT 给每个线程编号,换来独立寻址和标量风格的编程模型。问题是锁步执行:分支分歧时利用率掉到 1/K,per-thread 寄存器文件也让多 warp 驻留的代价整条链都高起来。前 12 个测试里 thread_id 是个被动读回来的字段;kernel-exec 里它变成驱动每条指令产生不同结果的执行上下文。
小结¶
实际上有了 ai 之后学习的深度和广度是比以前幸福太多的,在大学已经很不喜欢问别人问题了,因为当时所处的环境几乎问了也是白问,那只能关注在自己能够解决的问题上,但是现在可以做更深更强的比较研究
而另一方面,在工作中经历的一些经验,特别是一些要求比较 solid 的经历的推敲也能让我在提问中把握更好的方向,当然从另外一个意义上我还有相当丰富的被 ai 愚弄的经验。因此我其实可以说没有 ai, 自己写代码的话,其实理解深度还不见得能像现在这样好,这似乎是个有点矛盾的地方。ai 在教导的时候非常喜欢逻辑跳步,但是有经验的人可以掰正过来。收获一些真正有价值的东西,但不实际写代码和看代码实在是发虚。这个矛盾也需要后续再探索。