Background
当前 TileLib / TileLang 模板实例化路径中,PTOAS 在 ExpandTileOp pass 内维护了一份进程内缓存:
ExpandState::specCache 以 SpecKey 为 key 缓存已经实例化出来的 func.func
- 命中时直接复用当前 module 中已克隆进去的实例函数
- 未命中时,再调用
python -m tilelang_dsl.expand_helper ...,解析输出 MLIR,克隆到 module 末尾,然后写回缓存
这套机制当前能避免“同一 module 内相同 Tile op 重复实例化”,但它的职责位置更偏 PTOAS 内部实现,而不是 DSL 框架集成边界。
从架构上看,VPTO IR / TileLib 实例缓存更适合作为 DSL 框架侧能力,而不是依赖 ExpandTileOp 的进程内 DenseMap:
- 当前 cache 生命周期只在一次 pass / 一次 ptoas 进程内有效
- cache 命中结果绑定的是 MLIR 符号克隆结果,不是一个更稳定的框架层 artifact
- 上层框架很难复用已有实例,也无法提前控制 cache key / cache invalidation / artifact 存储
Problem
把 cache 放在 PTOAS ExpandTileOp 内部,会带来一些边界和演进问题:
- 同一个模板实例跨 module / 跨进程 / 跨编译任务无法复用
- 框架侧看不到也控制不了实例缓存,无法把它纳入自己的编译缓存体系
- 当前
SpecKey、函数命名、helper 调用协议、module clone 逻辑被耦合在一起,导致 cache 语义更像 pass 内部优化,而不是稳定集成契约
- 一旦未来希望由框架直接产出或持久化 VPTO authoring IR / 实例化结果,现在的 pass 内缓存无法自然承接
- cache 命中与否影响
ExpandTileOp 的性能,但这一性能行为当前不能由 DSL 框架统一管理
Suggestion
建议把“实例化结果缓存”的主责迁移到 DSL / 上层框架侧,PTOAS 侧只保留最薄的一层消费逻辑。
1. 明确 cache 的归属边界
建议定义成:
- DSL / 框架侧负责根据稳定协议计算 specialization key
- DSL / 框架侧负责决定是否命中、如何持久化、如何失效
- PTOAS 侧负责消费实例化结果,做 IR 接入、校验、后续 lowering
2. 将当前 pass 内 cache 语义降级为实现细节或过渡机制
ExpandTileOp 内部可以保留一个很轻量的“同进程去重”机制,但不应继续作为主要 cache 语义来源。
也就是说,理想状态应该是:
- 主要实例缓存由框架侧管理
- PTOAS 接收“已经选好并实例化好的 artifact”或更明确的 DSL 产物
- pass 内缓存即使存在,也只是本地优化,而不是对外协议的一部分
3. 把 cache key 协议文档化并前移到框架侧
当前实现中已经隐含了一套 key 规则:
- Tile:
dtype + shape + valid_shape + memorySpace + config
- View: 只按
dtype 进入 key,shape/strides/memorySpace 只参与约束检查
- Scalar:
dtype
- 另外还有
target、op、context_attrs
建议把这套规则整理成正式协议,并明确:
- 哪些字段决定实例边界
- 哪些字段只参与校验
- key 变更时 cache invalidation 怎么做
- DSL 版本 / 模板版本是否需要进入 key
4. 明确 PTOAS 消费的 artifact 形式
建议讨论并选定一种更稳定的框架侧产物,例如:
- 已实例化的 authoring-form MLIR 函数
- 可 import 的 TileLib 实例 module
- 或者更高一层的“模板实例描述 + IR artifact”组合
核心是让 PTOAS 面向“明确产物”而不是面向“内部 helper 调用 + 进程内 cache”。
Expected outcome
- VPTO IR / TileLib 实例缓存成为 DSL 框架能力的一部分,便于跨任务、跨模块复用
- PTOAS 与 DSL 框架之间的边界更清晰:框架产出实例化 artifact,PTOAS 负责消费和 lowering
- 后续如果要做编译缓存、远程缓存、增量编译、预热实例库,会更自然
ExpandTileOp 不再承担过多“框架缓存层”的职责
Possible acceptance criteria
- 明确一份框架侧 specialization key 协议,并与 PTOAS 当前实现对齐或完成迁移设计
- 设计并落地一种可由框架持久化的实例 artifact 形式
- PTOAS 能消费该 artifact,而不是必须自己调用 helper 才能命中缓存
ExpandTileOp 中现有 specCache 要么退化成局部优化,要么被新的消费路径替代
Additional context
当前现状可以直接从仓内实现中看到:
lib/PTO/Transforms/ExpandTileOp.cpp 中 ExpandState::specCache
buildOperandSpecsJson() / buildContextAttrsJson() 负责构造 helper 输入
invokeTilelangDSL() 负责 cache check -> 调 helper -> 解析 MLIR -> clone 到 module -> 回填 cache
docs/designs/ptoas-tileop-expand-design.md 3.2.1 / 3.2.2 已经描述了现有 SpecKey 和缓存策略
Background
当前 TileLib / TileLang 模板实例化路径中,PTOAS 在
ExpandTileOppass 内维护了一份进程内缓存:ExpandState::specCache以SpecKey为 key 缓存已经实例化出来的func.funcpython -m tilelang_dsl.expand_helper ...,解析输出 MLIR,克隆到 module 末尾,然后写回缓存这套机制当前能避免“同一 module 内相同 Tile op 重复实例化”,但它的职责位置更偏 PTOAS 内部实现,而不是 DSL 框架集成边界。
从架构上看,VPTO IR / TileLib 实例缓存更适合作为 DSL 框架侧能力,而不是依赖
ExpandTileOp的进程内DenseMap:Problem
把 cache 放在 PTOAS
ExpandTileOp内部,会带来一些边界和演进问题:SpecKey、函数命名、helper 调用协议、module clone 逻辑被耦合在一起,导致 cache 语义更像 pass 内部优化,而不是稳定集成契约ExpandTileOp的性能,但这一性能行为当前不能由 DSL 框架统一管理Suggestion
建议把“实例化结果缓存”的主责迁移到 DSL / 上层框架侧,PTOAS 侧只保留最薄的一层消费逻辑。
1. 明确 cache 的归属边界
建议定义成:
2. 将当前 pass 内 cache 语义降级为实现细节或过渡机制
ExpandTileOp内部可以保留一个很轻量的“同进程去重”机制,但不应继续作为主要 cache 语义来源。也就是说,理想状态应该是:
3. 把 cache key 协议文档化并前移到框架侧
当前实现中已经隐含了一套 key 规则:
dtype + shape + valid_shape + memorySpace + configdtype进入 key,shape/strides/memorySpace只参与约束检查dtypetarget、op、context_attrs建议把这套规则整理成正式协议,并明确:
4. 明确 PTOAS 消费的 artifact 形式
建议讨论并选定一种更稳定的框架侧产物,例如:
核心是让 PTOAS 面向“明确产物”而不是面向“内部 helper 调用 + 进程内 cache”。
Expected outcome
ExpandTileOp不再承担过多“框架缓存层”的职责Possible acceptance criteria
ExpandTileOp中现有specCache要么退化成局部优化,要么被新的消费路径替代Additional context
当前现状可以直接从仓内实现中看到:
lib/PTO/Transforms/ExpandTileOp.cpp中ExpandState::specCachebuildOperandSpecsJson()/buildContextAttrsJson()负责构造 helper 输入invokeTilelangDSL()负责 cache check -> 调 helper -> 解析 MLIR -> clone 到 module -> 回填 cachedocs/designs/ptoas-tileop-expand-design.md3.2.1 / 3.2.2 已经描述了现有SpecKey和缓存策略