经年治世

西风烈,长空雁叫霜晨月

同一个最终状态,为什么需要两次写出?

持续状态、最终状态与直接输出,为什么不是同一种缓冲。

系列目录 · 资源与调度 · Read in English “最终隐藏状态已经是图输出,为什么还要保存一次?” “因为图输出交给调用者,持久化状态交给下一次调用。” “那把两次保存串起来不就行了?” 听起来像一次很普通的公共子表达式复用。可状态型计算里,两个目的地拥有不同生命周期;把它们误写成一条链,可能把值的意义和副作用混在一起。 先把三个对象分开 对一个循环单元,可用教学关系描述: ......

地址能表示,为什么编译器说“内存满了”?

区分可编码地址范围、分配区间和真实内存预算。

系列目录 · 资源与调度 · Read in English “我只是生成一个稍大的输出,编译器却说全局内存用完了。” “它知道运行时会给多少内存吗?” “它知道一个默认数字。” 这段对话的危险之处在于,那个默认数字往往存在很久,以至于看起来像硬件真理。它可能最初服务于某个部署环境,后来却被一路复制成所有模型必须遵守的编译期上限。 三个不同的问题,被挤进了一个 capacity 地址分配至......

视图没有搬数据,为什么内存仍然不能还?

跨过视图链追踪异步消费者,防止过早回收底层存储。

系列目录 · 资源与调度 · Read in English “这个张量的最后一个用户已经过去了,可以回收。” “最后一个用户只是换了个形状。真正读数据的算子,还在另一条队列里排队。” 如果给内存分配器颁发“过度热心奖”,这类提前复用很有竞争力。它严格按 IR 出场顺序办事,看到直接用户都处理完了,立刻把空间交给下一个写入者。唯一的问题是:用户消失了,读者没有消失。 有三个容易混淆的结束时......

图里只是多了几个分叉,为什么同步寄存器先耗尽了?

分叉与依赖汇合如何形成同步资源压力。

系列目录 · 资源与调度 · Read in English “算术量没有增加多少,怎么连代码都生成不出来?” 白板上的图非常无辜:一个生产者,接着很多消费者。内存还有余量,计算单元也没增加,可依赖资源池先亮起红灯。把并行程序当成“节点加连线”画出来很容易,麻烦在于每条线最终都要找到一种设备可以理解的等待方式,而同步对象未必无限。 图的边不必等于一个物理同步槽 设搬运引擎 D 产生一份数据......

那只“临时”寄存器,为什么总被别人借走?

用作用域表达寄存器所有权,明确自动归还的边界。

系列目录 · 资源与调度 · Read in English “我只借一个寄存器,装一下地址,马上还。” “巧了,外面的循环计数器也只借了同一个。” 这类争执不会出现在代码评审聊天里。它会出现在机器码里:循环次数突然变成一段地址,或者配置加载覆盖了尚未使用的标量值。每个辅助函数单独看都很短,拼起来却像一间没有借用登记簿的工具房。 问题不是程序员记不住寄存器名字,而是名字没有表达所有权。字符串......

权重跑到左边以后:用一个转置恒等式,搬动整个编译链

转置恒等式只是起点,权重命名与量化轴还要一起移动。

系列目录 · 算子与布局 · Read in English “都是矩阵乘法,权重放左边怎么就不能编译?” 从线性代数看,这是个合理的疑问。从后端看,矩阵单元的数据流、权重打包、偏置参数与量化通道可能都围绕 动态输入 @ 常量权重 组织。当常量跑到左侧,算子名字没变,原有实现的假设却几乎全被触碰。 转置恒等式与形状证明 最直接但危险的方案是交换两个输入。矩阵乘法通常不满足交换律,连输出形状......

MatMul 后面有个 Sigmoid:为什么编译器偏偏不肯融合

多消费者、视图和依赖边界如何限制矩阵乘激活融合。

系列目录 · 算子与布局 · Read in English “卷积都能融合,矩阵乘法后面这个 Sigmoid 总可以吧?” 有时可以,有时编译器认真地说不。最有价值的优化文章,不只有成功合并后的漂亮图,还应该解释那些故意留下的节点。它们常常标记着真正的语义边界。 直接邻接与语义屏障 最简单的教学图为: 1X:[B,M,K] -- MatMul(W:[K,N]) --> U:[B,M......

把激活函数塞进卷积:少一个节点之前,先保住那条量化边界

融合卷积与激活之前,保住中间量化与合法形状。

系列目录 · 算子与布局 · Read in English “这两个节点挨着,融合以后应该会快吧?” “可能。先说清楚中间那个张量原来是什么。” 融合最诱人的画面是删掉图上的一个圆圈。可被删掉的不只是一个名字,还可能是一段真实的存储、一次量化、一种数据类型和一个被其他节点读取的值。若只把后处理开关打开,忘记中间语义,图确实更短了,答案也可能更远了。 先检查融合前提 教学计算为 u=Con......

谁动了我的临时寄存器:让发射器学会借东西、还东西

从手写临时寄存器到明确的借用、释放和嵌套约束。

系列目录 · 算子与布局 · Read in English 单独测试时,两个代码生成函数都很好。把它们组合起来,结果突然不对。第一个函数说:“我只是借个临时寄存器存基址。”第二个函数说:“我也是。”问题是,它们借的是同一个,而且谁也没问谁什么时候还。 字符串寄存器名特别方便:写起来短、看起来直接。它也没有所有权,无法告诉你某个名字现在是否装着仍然要用的地址、循环次数或中间值。当内核开始调用......

Abs 和 Sign:算术只有一行,参数协议却不能复制粘贴

一元算子的数学定义,需要贯穿类型、量化和参数编码。

系列目录 · 算子与布局 · Read in English 如果让人选两个最容易实现的算子,Abs 和 Sign 很可能榜上有名。一个取绝对值,一个判断正负。于是有人决定从现成二元算子复制一份代码,把两个输入都填成同一个值,再换一下操作码。 编译成功了。结果也出来了。接下来,那个本该最简单的算子,花掉了一整段调试时间。 真正的疑问是:“二元参数结构长得一样,是否意味着每个字段在一元指令里含......