系列目录 · 资源与调度 · Read in English
“矩阵乘完再切一小块,不如先切权重,再做小矩阵乘。”
“公式没错。切出来的新权重,用哪一组量化参数?”
“参数不是跟名字走的吗?”
白板上的代数变换往往十分干净。可编译器中的值除了形状和元素,还背着尺度、零点、通道轴、布局、存储和来源信息。省下的乘法如果把这些信息甩在身后,优化就会从“更快”变成“更难发现错误”。
从一条正确的浮点恒等式开始
设 X 的形状是 B×K,W 的形状是 K×N,只需要输出的某些列:
1 | slice_columns(X * W) = X * slice_columns(W) |
在合适的切片条件下,这个变换能减少计算或中间输出。若多个消费者分别取不同列,还可能把一个大矩阵乘拆成多个小矩阵乘。
但等式只描述数值运算,没有描述量化参数如何变化。假设 W 按输出通道量化,每列有自己的 scale 与 zero_point,那么选出列集合 J 时,参数也必须按同样的 J 选择。重新命名出来的权重对象,如果只是复制字节和形状,却没有登记派生量化信息,下游就可能查不到参数,或者误用不属于这些列的尺度。
这类缺陷很容易在浮点测试里隐身。浮点张量没有那条额外的每通道参数链,代数恒等式自然成立。
已核实的处理,比注释看起来更保守
相关提交在重写逻辑附近加入说明:拆分量化权重会产生带新名字的权重对象,而派生的每通道量化信息尚未安全传播。同时,它把整个“矩阵乘加切片”模式从规范化模式注册列表中移除。
这一点必须讲准确。代码里出现过一个只针对量化信息的条件草案,但它是注释状态;真正生效的动作是停用整个模式,而不是“只对量化权重跳过,浮点仍照常优化”。
因此公开叙述不能把它包装成精细的量化感知改写。更忠实的结论是:在派生元数据闭环尚未建立之前,选择让原始图结构保留。这是一种范围较大的正确性保护,可能放弃某些本来安全的优化机会。
该提交没有新增专门回归测试。我们能确认模式注册发生变化,不能仅靠这次修改声称所有受影响模型已完成数值验证。
为什么全局名字查询会放大重写成本
如果量化信息依靠名字去外部表查询,重写产生一个新值就不只是新建 IR 节点,还要同步维护命名与元数据映射。复制、切片、拼接、转置都会对这个映射提出不同要求。
可考虑的通用方向是把语义关键参数尽量放在类型或显式属性中,使重写函数必须处理它们。即便仍需要外部数据,也应提供“派生权重”的统一 API,返回值、量化参数和来源映射一起提交,避免各个模式自行补名字。
这只是设计建议,不是所读提交已经实现的架构。把参数搬进类型也不是万能答案:大数组会影响 IR 体积、序列化和比较成本,跨通道轴变换仍需要正确推导。
另一种优化:不要把广播常量铺满整个张量
同组历史还出现一处不同性质的改动:尺度与偏置的参数原先被按输入形状展开,后来改为保留通道向量,并构造紧凑的广播形状。
用一个教学输入 B×T×C 说明。若每个通道的系数在所有批次和位置相同,把 scale 从 C 个元素展开成 B×T×C 个元素,在数学上仍然正确,却把重复信息实体化了。若教学取 B=2、T=37,元素数就相差 74 倍;这只是形状推导出的存储比,不是任何实测加速比。
紧凑表示可减少常量生成、保存和搬运的潜在成本。但它依赖一个明确前提:后续算子理解该广播形状,并且通道轴已经规范化到约定位置。若输入布局仍有多种含义,简单固定成尾部通道形状,就可能把“减少内存”变成“沿错误轴缩放”。
两类改动其实都在问:谁承担语义?
权重切片的错误方向是丢掉必须随值变化的元数据;参数展开的低效方向是把本来可以由广播规则表达的信息重复存储。前者需要保留更多语义,后者需要减少冗余表示。
因此“IR 越简单越好”不够准确。一个小节点可能隐藏未被验证的广播假设;一个看似冗长的矩阵乘加切片,反而完整保留了当前系统理解的量化关系。
评审优化时,可以先列出不变量:哪些维度被选择,哪些通道参数必须重排,输出类型如何更新,布局是否变化,数值范围是否改变,是否仍能找到所有派生权重。只有这些问题回答完整,才讨论乘法数量和字节量。
一个具体反例,比十个随机模型更有力
教学权重有三列,其 scale 分别为 s0、s1、s2,且三者明显不同。只取最后两列。正确的新参数应是 s1、s2;如果误用 s0、s1,输出形状仍然正确,数值却按不同倍率偏移。
将所有 scale 设成同一个数,会让错误映射看起来没有影响。因此测试数据要主动打破对称性:不同通道不同尺度、不同零点、切片从非零位置开始,再加入不等长分块。这样的反例比一个很大的随机图更能解释元数据传播哪里断了。
广播测试也应该避免各个轴长度都一样。若 B、T、C 恰好相等,错轴广播可能仍通过形状检查。选择彼此不同的小维度,能让语义更容易观察。
什么时候可以重新打开这个优化
至少应具备四个条件:派生权重元数据有可靠来源;切片轴与量化轴关系被明确处理;后续 lowering 能消费新参数;回归测试覆盖有意构造的非对称反例。
之后再选择策略。可以先仅允许每张量量化,或仅允许沿非量化轴的特定变换;也可以为每通道切片实现完整参数传播。无论哪一种,都应由生效代码和测试证明,不应把注释中的理想条件当作当前行为。
性能收益还需与更多小矩阵任务、重复输入读取、内核启动和同步成本比较。少做若干乘法,不保证端到端更快,尤其当拆分破坏了大矩阵计算的利用率时。
给派生元数据画一张账单
假设一个重写会创建两个新权重。评审时可以给每个新值列一张账单:原始元素来自哪个范围,形状如何变化,量化轴是否移动,尺度和零点从哪里取得,调试来源如何映射,后续序列化使用哪个标识。任何一项写不出来,都说明重写的语义还没有闭合。
这张账单也能帮助区分“无法传播”与“暂时没有实现传播”。前者可能意味着该变换在某种量化表达下不合法;后者则可以通过增加明确的数据变换恢复优化。把两种情况都写成简单的 unsupported,会使未来维护者不知道该补算法还是该收紧适用范围。
广播常量也值得做同样的账单。紧凑 scale 向量保存的是哪个轴的参数,bias 的量化尺度与输入还是输出关联,标量参数扩成通道向量后是否保持原来的数值含义,都应明确。常量全零时容易出现“反正怎么广播都是零”的假安全感,测试还需要非零、非对称参数。
最后再看计算图:如果多个切片覆盖了几乎所有输出列,拆分可能没有减少多少总乘法,反而增加调度与读入次数;如果只取很小一部分,优化潜力才更清楚。适用条件、语义账单和成本模型,是重新开启模式前的三份不同材料,缺少任何一份都不应靠标题里的“优化”补上。
回归矩阵与实验
| 主题 | 必测组合 | 关注点 |
|---|---|---|
| 权重切片 | 浮点、每张量量化、每通道量化 | 语义允许范围 |
| 参数传播 | 非零起点、不等分块、不同尺度 | 参数索引与列选择一致 |
| 图结构 | 单用户、多用户、部分输出 | 不遗漏仍需要的列 |
| 广播 | 不同轴长度、标量参数、通道参数 | 正确沿约定轴扩展 |
| 表示成本 | 展开常量与紧凑向量 | 元素数、产物大小、编译内存 |
| 端到端代价 | 大任务与多个小任务 | 总时间与访存,不只算术量 |
建议先在独立的低维整数参考程序中验证量化切片等价性,再做紧凑参数与展开参数的逐元素比较。性能实验则分别测编译时间、常量体积、运行搬运和计算时间,避免把某一个指标的下降称作整体加速。
有时最负责任的优化补丁,是先把一个优化关掉。真正值得恢复的,不只是白板上的等式,而是等式背后那整套可以被验证的语义。
If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !