MLIR 静态内存规划器:GSoC 2026 之旅
内容提要
本文介绍GSoC 2026项目:为MLIR构建静态内存规划器。该工具在bufferization后利用显式缓冲区生命周期,将多个堆分配合并为单一静态arena,并用memref.view零开销重解释切片,适用于禁止或慢速堆分配的目标。实现分五个PR推进:核心pass与trivial/best-fit规划算法、arith.select释放链与反向别名保护、集成BufferViewFlowAnalysis以支持scf.if、scf.for测试覆盖、边界与错误用例。未来方向包括放宽入口块限制、路径敏感的生命周期收紧及内存空间感知。
延伸解读
为什么需要静态内存规划
文章指出,在嵌入式CPU、DSP和定制加速器等目标上,堆分配要么不被允许,要么因访问主存昂贵而缓慢。即使可用,频繁的小块分配也会带来碎片和缓存压力。该规划器在bufferization后利用显式生命周期,将多个堆分配合并为单一静态arena,并用memref.view零开销重解释切片,从而规避上述问题。
从手写DFS到统一别名分析
项目初期用前向DFS追踪arith.select的释放链,但遇到scf.if等结构化控制流时失效。导师指出BufferViewFlowAnalysis已统一建模arith.select、scf.if/scf.for、cf分支和view操作。作者删除手写DFS,改用resolve和resolveReverse两次调用,既简化代码,又免费获得未预料模式的正确性。这提示优先复用现有分析基础设施。
保守锚定与反向别名保护
对于嵌套在scf.if体内的dealloc,规划器用findAncestorOpInBlock将生命周期终点锚定到外层scf.if,保守但正确。反向别名保护则确保:仅当dealloc可能释放的所有alloc都由arena管理时,才可删除该dealloc。若存在嵌套的非arena分配,相关alloc会被保守跳过,避免误编译或泄漏。
当前限制与未来方向
目前仅规划函数入口块中的alloc,嵌套在scf.if或scf.for体内的分配会被跳过。未来工作包括放宽入口块限制、引入基于DominanceInfo的路径敏感生命周期收紧,使互斥分支共享arena槽位;以及支持内存空间感知,为异构目标的不同内存区域分配缓冲区并遵守容量约束。规划器函数是纯函数,便于替换更强算法。
Q&A
MLIR静态内存规划器的主要作用是什么?
该工具在bufferization之后,利用显式的缓冲区生命周期,将多个堆分配合并为一个单一的静态arena,并用memref.view零开销地重解释切片,从而消除冗余的堆分配。
为什么在加速器编译中需要静态内存规划?
因为许多目标(如嵌入式CPU、DSP、自定义加速器)要么不允许堆分配,要么堆分配很慢,且访问主内存开销大;即使可用,单个小分配也会因碎片和缓存压力损害性能。静态arena在编译时计算布局,避免了这些问题。
静态内存规划pass是如何工作的?
该pass是一个FunctionOpInterface pass,按函数运行,分六步:收集候选alloc并找到dealloc;构建包含大小、对齐、生命周期的描述符;运行规划算法(trivial或best-fit);分配字节偏移;创建arena;重写alloc为memref.view并删除dealloc。
规划器支持哪些内存规划算法?
支持两种算法:trivialMemoryPlanner(简单的顺序打包)和bestFitMemoryPlanner(考虑生命周期重叠,尝试复用arena槽位)。两者都是纯C++函数,可独立单元测试和替换。
如何处理arith.select产生的dealloc链?
通过前向DFS遍历use-def图,沿BufferViewFlowOpInterface找到可达的dealloc,并添加反向别名安全保护:只有当dealloc可能释放的所有alloc都由arena管理时,才能安全删除该dealloc。
BufferViewFlowAnalysis在项目中起到了什么作用?
它是一个统一的别名分析,建模缓冲区通过arith.select、scf.if/scf.for、cf分支和view操作的流动。项目用它替换了手写DFS,通过resolve和resolveReverse两个调用分别获取前向和反向别名集,简化了代码并自动支持多种控制流模式。
当前规划器有哪些限制或未来改进方向?
目前只规划函数入口块中的alloc。未来方向包括:放宽入口块限制、使用DominanceInfo进行路径敏感的生命周期收紧以让互斥分支共享槽位、插入更强的规划算法(如区间图/图着色、minimalloc),以及支持内存空间感知的分配。