
1. 别急着写卷积先算清楚这笔“计算账”很多朋友第一次自己动手写卷积网络时心里想的是不就是拿一个窗口在特征图上滑来滑去把对应位置乘起来再求和吗用两层for循环加一层累加几行代码就能搞定。一开始确实能跑通可一旦把输入尺寸放大、通道数增多网络训练和推理的速度会突然暴跌甚至让你怀疑是不是代码写错了。问题不在于“卷积的逻辑没写对”而在于“卷积的计算规模远超直觉”。以一张 112×112 特征图、64 个输入通道、64 个输出通道、3×3 卷积核为例单层一次前向推理的乘法累加次数大约是输出特征图大小112×112每个输出点需要累加64 输入通道 × 9 个卷积核位置 576 次单层总 MAC 数112×112×64×576 ≈ 4.7 亿次这还只是 64 通道的小模型层放到现代分类网络或检测网络里动辄几百个卷积层计算量轻松突破百亿甚至千亿级别。所以“卷积慢”的真实原因从来都不是单个乘加运算贵而是规模太大、访存太多。想真正把卷积加速先得理解这笔账是怎么算的以及每一笔开销到底花在了哪里。我见过不少人一上来就调库跑基准测试看到某框架比另一框架快就认为是语言或编译器差异。实际上框架调用底层算子库时是否走 im2col 重排、是否启用 Winograd、是否把多个算子融合成一个 kernel这些才是真正拉开性能差距的地方。本文会沿着“算法优化→硬件适配→稀疏化改造→工程落地”这条线把卷积高效实现里最关键的部分拆开讲清楚。1.1 卷积的两类开销计算开销与访存开销计算开销很好理解就是乘加次数。访存开销则经常被忽略——卷积计算过程中需要反复读取输入特征图、卷积核权重再写回输出结果。现代处理器的 ALU算术逻辑单元运算速度远远快于内存带宽所以很多情况下卷积的真实瓶颈并不是“算不动”而是“数据喂不过来”。举个夸张但真实的例子如果按最笨的方法逐点滑动窗口计算卷积同一个输入像素会被相邻窗口重复读取好多次3×3 卷积核下每个像素平均要被读 9 遍。这就像你做饭时需要从冰箱里反复拿同一棵葱每次只切一小段大部分时间都耗在了开冰箱门上。所以高效卷积实现的本质就是两种手段的组合要么减少乘加次数算法层面要么减少重复访存硬件与算子层面。有了这个视角再去看各种加速方案就清楚多了im2col 是用空间换时间把卷积转成矩阵乘法以利用高度优化的矩阵乘库Winograd 是减少乘法次数但会增加加法操作FFT 是把空间卷积变成频域点乘硬件里的数据复用则是通过缓存和脉动阵列把数据留在“手边”反复利用。1.2 为什么矩阵乘运算这么受重视这里需要多说一句现代 CPU 和 GPU 对矩阵乘GEMM的优化程度远超对任意卷积的直接实现。原因在于矩阵乘的访存模式非常规则——两个矩阵的每个元素都可以被反复复用数据流清晰容易做分块tiling和并行化。而直接卷积的窗口滑动模式在内存访问上是跳跃的缓存命中率往往不高。因此主流通用方案是先把卷积“变形”成矩阵乘再去调用已经优化到极致的 GEMM 算子。比如把一个 3×3 卷积的输入特征图重排成一个大矩阵每一行对应一个输出位置的输入窗口再乘上若干个小矩阵最终得到输出。这个过程就是 im2col。理解了“卷积慢在访存、规律性强、适合转化为矩阵乘”这三个事实后面的所有优化思路都顺理成章了。2. im2col、Winograd 与 FFT算法层的取舍逻辑2.1 im2col用“重排”换取“规则”的经典招数im2col 的全称是 image to column核心操作是把输入特征图中每个卷积窗口内的数据“拉直”成一列最终把整个卷积变成一次大矩阵乘法。假设输入是 H×W×C卷积核是 K×K输出通道是 N那么 im2col 构造出的矩阵形状大致是 (H_out × W_out) × (C × K × K)乘以权重的 (C × K × K) × N得到输出矩阵。这样做的好处非常明显GEMM 库的优化程度远高于手写卷积而且由于数据被重排在连续内存里向量化指令和缓存预取都能发挥得更好。但代价也很直接——重排后内存占用会膨胀约一个数量级。3×3 卷积核意味着每个输入像素会被复制 K×K 也就是 9 份加上 padding 和 batch 维度哪怕只有几十 MB 的特征图经过 im2col 也可能变成几百 MB 的临时矩阵。所以实际工程里成熟的算子库很少会把整张特征图一次性全部展开而是采用分块 im2col每次只展开一小块足够放入缓存的数据完成该块的矩阵乘以后再处理下一块。这样既保留了 GEMM 的规则访存优势又不会让内存爆炸。分块大小的选择通常取决于目标硬件的缓存容量和矩阵乘单元的大小需要逐个平台调参。2.2 Winograd把乘法的账算到极致如果说 im2col 是在“数据排列”上做文章那 Winograd 就是在“数学变换”上偷计算。先抛结论对常见的 3×3 卷积Winograd F(2,3) 能把每个输出点的乘法次数从 9 次降到约 4 次乘法量减少超过一半。Winograd 的原理可以类比“多项式乘法换种方式算”。对于 F(2,3)即输出 2 个点、卷积核 3 个点的情况原本需要 2×36 次乘法但通过特定的输入变换、权重变换和输出变换可以把乘法次数降为 4 次。付出的额外代价是变换阶段会引入大量加法运算并且论文推导出的变换矩阵里会出现很多非整数的系数。这也带来了两个工程问题。第一数值精度会受影响变换系数导致浮点运算次数增加时舍入误差会被放大对训练敏感或需要高精度的场景必须做误差评估。第二小卷积核才划算1×1 卷积没有窗口展开用 Winograd 没有任何收益5×5、7×7 等大卷积核虽然也能用但变换矩阵复杂度急剧上升业界普遍只在 3×3 卷积上采用。PyTorch 等框架在 CPU 后端的某些 3×3 卷积路径上实际上已经默默启用了 Winograd 算法这就是为什么有时你用 profiling 工具看卷积层的乘加次数和理论值对不上。2.3 FFT 与分离卷积特定场景才派得上用场FFT快速傅里叶变换的思路是把空间域的卷积转换成频率域的逐元素相乘。理论上对于很大的卷积核例如 7×7、11×11FFT 能把复杂度从 O(K²) 降到 O(K log K)看起来非常诱人。但现代卷积网络几乎不用大核加上 FFT 需要把实数特征图变换成复数域内存开销成倍增加变换本身的额外耗时也很高。所以 FFT 在深度学习里的位置很尴尬——它更适合传统图像处理的滤波场景在主流网络结构里基本退居幕后。分离卷积的思路则完全不同把 N×N 的标准卷积拆成一个 N×1 卷积加一个 1×N 卷积等效感受野基本不变但计算量大幅下降。这种做法直接改变了网络结构而不是算法实现因此更像是一种架构层面的“卷积高效化”。真正要落地时还得看硬件对非标准卷积核的支持程度。有些加速芯片对 3×3 有专门的算子流水线拆成 1×3 和 3×1 反而可能降低利用率所以工程上必须“看硬件下菜碟”。3. 硬件视角从数据搬运到算子融合的降本增效3.1 先搞懂“算术强度”再谈硬件优化很多讲卷积优化的文章喜欢直接抛出“要利用数据复用”“要做 tiling”之类的结论但没说清楚为什么。这里面有个最重要的指标叫算术强度arithmetic intensity定义是总计算量除以总访存量。卷积的算术强度通常远高于全连接层因为同一块输入数据会被多个输出通道复用——这是卷积天然的优势。硬件实现要做的就是把这种复用潜力真正兑现。设想一个最粗暴的算子每计算一个输出点都把对应的输入块和权重从内存读一遍那算术强度立刻被拉低到接近 1:1算力再高也没用全部卡在内存带宽上。所以现代硬件设计的核心目标就是让数据在离运算单元最近的地方“转圈复用”而不是反复去内存里拉数据。一个典型技术是脉动阵列。简单说就是把大量乘法单元排成一个二维阵列数据从左边和上边“流动”进阵列每个单元只和相邻单元通信同一个输入数据可以沿阵列一路传递被很多计算单元复用。这种设计极其适合卷积里“一个像素乘多个权重”的访问模式。CPU 的 SIMD 指令、GPU 的张量核心走的也是类似逻辑数据搬进寄存器或共享内存后一次性参与多组乘加运算。3.2 Tiling 分块把“小数据”留在缓存里理解了算术强度tiling 分块就很好解释了。特征图通常大得远超缓存容量如果直接展开成一个大矩阵去算缓存会被反复冲刷大量时间浪费在等内存数据上。正确做法是把输出特征图分成若干个小块每个块内的输入窗口和对应权重尽量一次性装入缓存算完再换下一块。分块大小的选择本质上是在“数据复用率”和“分块数量带来的调度开销”之间做权衡。块太小权重复用不充分每个块都要重新加载权重块太大超出缓存又退化成直接访存。常见的思路是按输出通道分块然后把输入空间维度也做分块形成二维 tile。在一些自定义加速器设计里工程师还会专门为卷积设计“滑动窗口 buffer”让输入数据像传送带一样被滚动复用从而避免重复读取相邻像素。我实际调试过不少算子最反直觉的一条经验是别盲目追求大分块。有一回我把某个卷积的 tile 从 64×64 改成 128×128本以为复用率高了会更快结果反而慢了几十个点。后来用性能分析工具一看L2 缓存命中率大幅下降原因是 128×128 的输入块加上权重和输出缓冲把缓存塞满了。真正合适的 tile 大小一定得结合目标设备的缓存层级和 GEMM 核心尺寸一起测。3.3 算子融合把多次读写的中间特征图“干掉”在只优化单个卷积算子时我们默认输入输出都存在全局内存里。但一个网络是由很多算子串起来的卷积输出后通常接激活函数然后接池化或下一个卷积。每经过一个算子数据都要被完整写入内存、再完整读回来这类开销叫“中间特征图搬运动”。算子融合就是把几个连续的算子合并成一个内核典型例子是 ConvBNReLU。BN 的参数在推理时可以折算进卷积权重和偏置里ReLU 则可以在卷积输出还在寄存器或缓存的时候就完成判断从而省掉一整轮全局内存读写。不要小看这一步省掉的往往是几 MB 甚至几十 MB 的特征图写入和读取在端侧低带宽设备上可能带来 20% 以上的端到端加速。这里还有一个更激进的融合思路把相邻卷积层直接整层合并计算。当上一层卷积的输出没有经过非线性激活或仅经过线性变换时两层卷积在数学上可以合并成一层更大感受野的卷积。不过现代网络里几乎都跟了激活函数所以这种融合更多出现在早期网络结构优化里。现在业界主流做法是让编译器自动识别可融合模式在计算图中插入融合规则而不是开发者手写每个融合算子的实现。3.4 量化与低精度计算用“位宽”换“吞吐”硬件层面的“高效”并不仅仅体现在数据复用上还体现在低位宽计算上。目前主流加速卡都支持 INT8 甚至更低精度计算单位时钟周期内能执行的整数运算次数通常是 FP32 的两倍以上并且内存带宽占用砍半。对卷积这类计算密集操作来说FP32 转 INT8 后不仅速度上去了功耗也跟着降下来。这里要特别提醒一点量化不是简单地做一次四舍五入。真正的流程包括校准、选择缩放因子、零点和截断范围还得考虑每层不同的数据分布。如果直接把某个已经训练好的权重转成 INT8精度往往掉得很快。常见做法是用几百张代表性数据跑一遍统计每层激活值的分布再按对称或非对称量化方式计算缩放系数。量化加融合加 tiling 这三板斧是部署一个卷积模型到端侧设备前最基本的性能优化组合。4. 稀疏化把“无效计算”从根上消灭4.1 为什么说卷积里藏着一半以上的“无用功”前面讨论的优化都是在让计算跑得更快但并没有减少总的乘加次数。如果卷积核里某些位置的权重本来就接近零或者某个通道的权重全部很小那这些乘加操作其实就是在做“无用功”。稀疏化想解决的就是这件事让零值直接跳过从根上削减计算量。这个方向不是空想。很多训练好的模型里权重分布呈现出显著的长尾效应大量数值趋近于零。早期研究里发现剪掉一部分小权重后再微调模型精度几乎不降某些剪枝率甚至能到 80% 以上。卷积层由于权重复用性强对剪枝的容忍度其实比全连接层更高这也是卷积稀疏化特别受关注的原因。不过要小心一个误区权重等于零不意味着计算可以自动省掉。在普通稠密算子里硬件依然会老老实实执行乘法乘上零的结果还是零但时钟周期和功耗照花不误。只有在算子或硬件层面显式识别这些零、跳过相关计算稀疏化才有真实收益。这就引出了稀疏化的核心难点——怎么让“跳零”变得比“硬算”更便宜。4.2 从非结构化稀疏到结构化稀疏一场工程化的妥协稀疏化大致分两个派别。一派叫非结构化稀疏把权重矩阵里绝对值的元素按阈值剪掉剩下非零元素的位置毫无规律。这种剪法精度最容易保住因为对模型的破坏最小但硬件支持极差——处理器喜欢规则的数据流遇到东一个西一个的零值要么加索引要么空转等待得不偿失。另一派叫结构化稀疏规定零元素必须按规律出现。最常见的包括按整行、整列或整块剪枝。比如“块稀疏”把权重按 2×4 或 4×4 分块整块全零才跳过更流行的是“通道剪枝”直接把一整个输出通道删掉。结构化稀疏牺牲了一部分精度空间但换来了硬件可实现的规则性这也是当前加速卡普遍支持的策略。现在不少推理框架的稀疏算子往往只对“固定比例、固定位置”的稀疏模式做优化比如行内每四个元素最多两个非零这样的约束。从我接触过的实际项目看给自己做算法选型时先看目标硬件对稀疏的支持程度再回头定剪枝策略顺序不能反。如果硬件只认 2:4 稀疏你却做了全局非结构化剪枝那最后只能靠软件层模拟“跳过零值”速度反而比稠密还慢。反过来如果只是想把模型压缩到更小体积不在乎推理加速那非结构化稀疏加无损压缩也能达到目的——但那是存储优化不是计算优化别混为一谈。4.3 激活稀疏另一片被低估的沃土权重只是稀疏化的一个维度激活值同样有大量零元素。尤其在 ReLU 这类修正线性单元之后负值全部被截断成零实测某些层的激活值稀疏度可以超过 50%。这意味着卷积计算时输入特征图里有大量零乘出来还是零整个输出也就不用算了。利用激活稀疏的做法通常是“跳过零块”。以卷积窗口在特征图上滑动为例如果窗口内所有元素都为零那这个位置的乘加可以直接跳过更进一步如果一整行或一整块输入区域都是零就可以把该区域对应的计算从执行计划里删掉。这在批处理batch比较小的实时推理场景下收益很明显因为每个样本的稀疏模式都不一样总能在统计意义上省掉可观的计算。但激活稀疏的实现难度比权重稀疏更高。输入数据的稀疏模式是在运行时才展现的系统无法事先静态规划需要实时检测零值区域、动态调整计算队列这极易造成线程束分支分化和负载不均。一个 warp 里的某些线程在算、某些线程在等GPU 就只能干瞪眼。所以目前激活稀疏主要用在 CPU 或特定 DSP 上配合稀疏矩阵存储格式来做条件跳转在 GPU 上反倒用得少。4.4 稀疏卷积的硬件想象力从“跳过”到“专用指令”硬件界对稀疏化的探索其实走了好几年。初期做法是在算子库层面做“稀疏感知”数据存储时只保存非零值和对应的坐标计算时用 gather 操作把非零数据收集齐再乘加。但 gather 本身也有开销数据越稀疏收益越高稀疏度不到一定程度就亏。后来的思路逐渐演进到硬件专用加速。某些研究机构给出过一种“稀疏卷积加速器”的架构设想存储器里保存压缩后的稀疏输入和权重经过解压缩流水线后把非零元素配对送入乘法阵列同时维持一个“零跳过”调度器确保计算单元始终在工作不因等待某个零值而空转。这类架构在仿真里的加速比能到稠密实现的好几倍但真正流片的产品还不多。原因很现实稀疏模式多种多样硬件一旦固定支持某一种模式能适配的模型就受限通用性远不如支持稠密卷积来得省事。所以现在业界的现实是稀疏化仍然是“模型压缩”阶段最常用的利器而要在推理阶段兑现加速必须从训练时就考虑结构化和硬件适配。很多团队的做法是训练时加正则化项诱导权重往目标稀疏结构收敛再用专门的稀疏算子导出导出部署。这条链路已经很成熟值得想用稀疏化提速的朋友认真研究。5. 稀疏化的代价工程链路里的真实摩擦5.1 稀疏格式的存储与带宽问题很多人以为把零值剪掉模型就自动变小、运算就自动变快最后发现根本不是这么回事。在部署阶段第一个要面对的问题就是稀疏张量怎么存。如果还按稠密格式存储零值占的位一个不少等于剪枝只“剪”了计算没有“剪”存储收益少了一大截。稀疏存储常用格式有坐标式COO、压缩稀疏行CSR、压缩稀疏列CSC等。拿 CSR 来说它把非零元素按行排列额外用一个偏移数组记录每行从哪里开始读大幅压缩了存储量。但代价是索引访问变成了间接跳转数据的局部性变差CPU 的缓存预取几乎失效。我在某个推理引擎里试过权重稀疏度在 70% 以上的模型加载权重倒是省了很多内存但推理速度并没有想象中提升那么多排查下来瓶颈就卡在稀疏格式的 gather 访存上。另外稀疏矩阵里的非零元素位置不固定编译器无法自动向量化。想用 SIMD 加速就得把非零元素先“打包”成连续数组再做元素对齐和掩码操作。这一套流程写出来容易真正塞进算子库并做到线程安全、误差可复现工作量非常大。5.2 负载均衡一“疏”各表忙闲不均均衡问题在稀疏加速里最让人头疼。假设你把一个卷积层按输出通道拆给多个处理器核心每个通道算出来的非零数量很可能大不相同。有的通道剪得干净只剩三五个非零几微秒就算完了有的通道非零密布要跑上百微秒。结果就是负载严重的核心忙得喘不过气其他核心早干完活了在旁边看戏整体加速比远低于理论值。这个问题在 GPU 上更明显。GPU 的调度单位是 warp一个 warp 里 32 个线程尽量执行同一条指令。如果这 32 个线程负责的数据稀疏程度不一样有的线程数据多、有的数据少硬件不得不把多数据线程拆分成多条指令执行产生大量“部分占用”周期。因此设计稀疏卷积算子时最好把稀疏度相近的通道分到同一个 warp 或同一个核心或者在输入侧做一下“重排”把稀疏度排列得均匀一些。这类优化听起来简单做起来极其琐碎不同模型的稀疏模式千奇百怪基本没法一次写死只能配启发式规则。5.3 剪枝后必须微调算法与硬件的“第二次握手”剪枝本身不是终点剪完了通常还得微调模型让剩余的权重“补位”。这个流程大家多少都知道但很少有人强调剪枝策略和硬件结构耦合得越深微调就越难。比如做 2:4 结构化稀疏时模型每一次前向都要维持“每四个元素里最多两个非零”的结构这意味着普通梯度下降更新权重后还要做一次额外投影操作把权重压回稀疏结构。这个额外步骤不仅增加训练开销还可能拖慢收敛。更麻烦的是非零位置如果在训练中固定不变模型表达能力受限位置如果动态变化硬件又不好做静态优化。业界目前的折中方案是先用普通剪枝加微调定下稀疏结构再固定该结构进行二次训练虽然缺少了一些灵活性但部署链路最可控。我自己的经验是做稀疏化前先算一笔“端到端收益”的账模型体积减少了多少推理延迟实际降了多少精度损失是否在可容忍范围内如果三者只能占两个那就要回头审视稀疏化的必要性。有时候剪枝加低秩分解配合量化比单纯追求超高稀疏度更划算。5.4 软件工具链稀疏化的“最后一公里”毫不夸张地说稀疏化最难啃的骨头是软件生态。学术论文里秀出 90% 稀疏度推断加速比时背后往往有一套定制算子库和编译器。落到真实部署时你的框架是否能调用这些算子库算子库是否支持目标硬件目标硬件的驱动是否妥善处理了设备内存的稀疏压缩……每一环都可能是断点。如果只是在自家推理引擎里玩可以自己写算子如果想借助通用的推理框架就得看它的稀疏算子实现程度、调试接口和性能计数。很多框架目前只对线性层和 1×1 卷积做了成熟稀疏加速3×3 卷积靠的还是 im2col 加稠密 GEMM稀疏化收益很难传导到卷积上。选型之前最好拿真实模型在目标硬件上跑一轮 profiling别被基准脚本里漂亮的数字骗了——那些基准往往只测了稀疏化最友好的几何配置。5.5 一个务实的落地路线基于上面这些摩擦我想给出一套自己实际用过的落地路径。第一步先用通道级剪枝把整个网络里冗余通道清掉这步能带来立竿见影的模型体积压缩并且几乎所有推理框架都支持。第二步对保留下来的大通道内部做块稀疏或 2:4 稀疏重点优化那些计算热点层。第三步统一做 INT8 量化和算子融合把前面剪完的模型再压一遍。这条路径的本质是先做最稳妥的粗粒度稀疏再做高难度的细粒度稀疏。粗粒度稀疏找的是结构冗余细粒度稀疏挤的是参数冗余两者结合才可能既保住精度又拿到加速收益。切忌一上来就搞全网非结构化稀疏否则大概率会把后续工程成本拖入泥潭。6. 对训练侧的反向影响——这会把我们带向何方6.1 稀疏化不应该只发生在训练之后很多人习惯把稀疏化当成一个“训练完了再处理”的部署技巧这个视角正在被越来越多的研究挑战。试想如果最终推理时的权重注定要满足某个稀疏结构那训练过程中为什么不能直接考虑这个约束呢有些工作已经在尝试把稀疏正则化拉进训练目标使权重在训练过程中逐渐向规则化稀疏结构收敛达到“训练完即可用、无需事后大动干戈”的效果。这条路的时间收益很高。传统流程里剪枝、微调、再剪枝要反复迭代好几轮每一次微调都是一次完整的训练过程。如果稀疏约束在训练早期就开始生效就可以省掉大量重复迭代。不过同时要接受训练效率的下降因为每次参数更新后都需要做结构映射或投影梯度传播过程也被改变收敛曲线会变得不太平稳。综合考虑现在很多团队更偏向“先普通训练再结构化剪枝再短微调”的务实套餐而不是在漫长训练全程都背着稀疏约束。6.2 稀疏性对模型结构设计的启迪另一个有趣的方向是稀疏化反过来影响我们设计网络结构的方式。传统网络为了“整齐”几乎全用规则稠密卷积。但既然最终硬件已经能处理某种稀疏模式那模型结构设计时就可以刻意安排“一部分通道密集、一部分通道稀疏”的配置比如在大卷积核分支上采用稀疏配置在小感受野分支上保持稠密让硬件资源和模型容量各取所需。这种做法有点像给网络做“局部麻醉”核心路径上保持高质量稠密计算外围路径上以低精度、低计算量方式补足信息。不少轻量化网络的思路其实就是在做这件事只是没有显式挂“稀疏化”的名头。从长远看算法和硬件的边界会越来越模糊稀疏结构很可能会成为网络架构搜索里的一个关键维度被自动搜索算法直接优化出来。6.3 训练时硬件加速的“未竟之地”最后聊一点训练侧的难点。目前稀疏化在推理侧已经有大量商业产品落地但训练侧的稀疏加速还远远不够成熟。原因在于训练不仅要做前向的乘加运算还需要反向传播计算梯度梯度矩阵往往不具备前向权重那样的稀疏性而且训练过程中权重一直在变稀疏模式也在动态变化想像推理那样静态压缩就很难。现在有些尝试是在训练时只让部分权重参与梯度更新近似计算梯度的一小部分以此降低反向传播计算量。这类方法确实能把训练速度提上去一些但收敛质量还需要大量实验调参。对大多数开发者而言现阶段更现实的做法还是在推理阶段享受稀疏化红利训练侧仍然用稠密计算保证模型精度。等到训练侧稀疏加速真正成熟卷积高效实现的版图才会再次迎来一轮大变革。我的一些实战体会写到这里我想把最想提醒大家的一句话放在最后卷积的高效实现永远没有银弹im2col、Winograd、量化、稀疏化这些招数全部都要看具体硬件和具体模型上下文。我自己曾经在一个边缘设备项目上花了整整一周做稀疏化好不容易把某层权重稀疏度压到 75%结果部署后发现推理延迟反而增加了原因是设备像素核处理器的分支跳转开销远大于省下的那点乘法时间。后来换成通道剪枝加 INT8 量化延迟才真正降下来。从那时起我养成了一个习惯任何优化方案动手前先写一个“假设计算模型”把目标硬件的访存带宽、缓存大小、支持的数据格式和指令特性摆出来再决定优化路线。如果你也正准备优化自己的卷积模型我的建议是从 profiling 开始先弄清楚每一层到底卡在计算还是卡在访存再针对性地选优化手段不要一上来就追稀疏化这种听着很厉害的方案。工具链的成熟度、团队的工程能力、目标的收益预期三者共同决定了最终成败。把基础的数据流动逻辑吃透再一点一点叠加高级技巧才是这条路上最稳的走法。