3、TinyML硬件约束:微控制器内存/计算限制、存储层次、稀疏性带来的潜在收益与挑战

TinyML 的部署目标并非通用处理器或 GPU,而是面向嵌入式场景的微控制器(MCU)与极低功耗处理器。这类硬件的资源边界决定了稀疏矩阵格式能否真正落地,以及推理收益的上限。理解 MCU 的内存容量、计算单元、存储层次与访存带宽,是评估稀疏格式收益的前提。本章从硬件约束出发,分析稀疏性在真实 MCU 上的收益来源与实现代价。

MCU 稀疏推理硬件约束与收益链路 硬件资源约束 SRAM / Flash 容量 KB ~ MB 级,权重/激活驻留 计算单元 MAC 单元少,无向量化宽 SIMD 时钟频率 16~200 MHz,功耗受限 存储层次 Flash→SRAM→寄存器,无 Cache 稀疏性转化路径 存储压缩 CSR/CSC/位图,减少 Flash 占用 计算跳过 跳过零值 MAC,减少指令周期 访存优化 减少 SRAM 读写次数,降低功耗 内存驻留 模型可完整放入 SRAM 运行 收益与挑战 收益 模型缩小 3~10x,推理加速 挑战 索引开销、不规则访存、分支 权衡 稀疏度阈值、格式选择、对齐 评估 需实测 Flash/SRAM/周期/功耗 图:MCU 硬件约束下稀疏矩阵推理的收益转化与风险链路

3.1 MCU 存储与计算资源边界

微控制器的硬件资源与云端处理器有本质差异。以典型的 Arm Cortex-M 系列为例,其 SRAM 容量通常在 16KB 到 512KB 之间,Flash 在 64KB 到 2MB 之间。相比之下,一个中等规模的 CNN 模型权重动辄数百 KB 到数 MB,这意味着模型可能无法完整驻留于 SRAM 中。更关键的是,MCU 没有独立于 CPU 的显存或大容量 DRAM,所有数据访问都直接面向 Flash 与 SRAM,且 Flash 读取速度远低于 SRAM,随机访问 Flash 的延迟可达几十个时钟周期。

计算资源方面,MCU 的 MAC(乘加)单元数量极少。Cortex-M4 系列仅有单周期 MAC 指令,Cortex-M7 支持双发射但依然无法与 DSP 或 GPU 的并行 MAC 阵列相比。多数 MCU 没有 SIMD 指令集(或仅有极窄的 SIMD),这意味着稀疏矩阵的“跳过零值”操作无法通过向量化掩码高效完成,而需要依赖循环分支判断。此外,MCU 的时钟频率通常在 16MHz 到 200MHz 之间,功耗预算从几毫瓦到几十毫瓦不等,这决定了推理延迟和能耗的硬上限。

核心约束总结:

  • 存储墙:模型权重与中间激活必须压缩到 SRAM/Flash 容量以内,否则无法部署。
  • 计算墙:MAC 单元数量少,无法通过并行度掩盖低效的访存模式。
  • 功耗墙:频繁的 Flash 读取和 SRAM 写回会显著增加动态功耗,影响电池寿命。

3.2 存储层次与访存代价分析

MCU 的存储层次通常只有三级:Flash(代码与常量数据)、SRAM(运行时数据)、寄存器(临时变量)。没有 L1/L2 Cache 的 MCU 中,CPU 直接访问 Flash 和 SRAM,每次访存的延迟和功耗差异巨大。以 Cortex-M4 为例,SRAM 访问延迟为 1~2 个周期,Flash 访问延迟为 2~5 个周期(带缓存时),但随机 Flash 访问可能触发预取失败,导致 10 个周期以上的惩罚。

稀疏矩阵格式的引入改变了访存模式。以 CSR(Compressed Sparse Row)格式为例,权重存储从密集的连续数组变为“值数组 + 列索引数组 + 行偏移数组”。这种结构在减少存储量的同时,引入了间接寻址。当 CPU 需要读取某个非零权重时,必须先读取列索引,再根据索引计算目标地址,这增加了访存次数。在无 Cache 的 MCU 上,这种间接寻址的代价可能抵消掉存储压缩带来的收益。

存储层次 容量范围 访问延迟 稀疏性影响
Flash 64KB ~ 2MB 2~5 周期(顺序),10+ 周期(随机) CSR 索引数组随机访问 Flash 时惩罚明显
SRAM 16KB ~ 512KB 1~2 周期 位图格式可减少 SRAM 占用,但需额外解码
寄存器 16~32 个通用寄存器 0 周期 稀疏解码循环需占用寄存器,增加压栈/弹栈

在实际部署中,需要根据权重数据的访问频率选择存储位置。频繁访问的稀疏索引应尽量放入 SRAM,而权重值本身可以压缩存储在 Flash 中,通过 DMA 批量加载到 SRAM 缓冲区。这种“双缓冲”策略可以有效隐藏 Flash 访问延迟,但增加了代码复杂度和内存占用。

3.3 稀疏性带来的收益:存储、计算与功耗

稀疏性在 MCU 上的收益主要体现在三个维度。首先是存储收益:当模型剪枝后稀疏度达到 80%~90% 时,CSR 格式可以将权重存储量压缩至原来的 20%~30%(考虑索引开销)。这意味着原本无法放入 256KB Flash 的模型,压缩后可以完整驻留,避免了外部 Flash 扩展或模型分片加载。其次是计算收益:跳过零值 MAC 操作可以直接减少指令周期。对于一个 3x3 卷积层,若稀疏度为 75%,理论上 MAC 次数减少 75%,推理延迟可降低 40%~60%(受索引开销影响)。最后是功耗收益:减少 Flash 读取和 SRAM 写回次数,直接降低动态功耗。在电池供电的传感器节点中,这可以延长设备续航时间。

然而,这些收益并非自动获得。稀疏格式的索引解码、分支判断、循环开销会消耗额外的指令周期。在 MCU 这种低并行度平台上,如果稀疏度低于某个阈值(通常为 60%~70%),稀疏格式反而比密集格式更慢。因此,部署前必须进行实测评估,而非仅凭理论计算。

经验阈值参考:

  • 稀疏度 < 50%:不建议使用稀疏格式,密集存储 + 循环展开更高效。
  • 稀疏度 50%~80%:可尝试位图格式或 CSC,需实测对比。
  • 稀疏度 > 80%:CSR/CSC 格式收益明显,但需注意索引类型宽度(8/16/32 位)。

3.4 稀疏格式在 MCU 上的实现挑战

在 MCU 上实现稀疏推理,面临四个主要挑战。第一是索引开销:CSR 格式需要存储列索引和行偏移,对于 int8 权重,如果使用 16 位索引,索引开销可能占原始存储的 25% 以上。对于小型矩阵(如 1x1 卷积),索引开销甚至可能超过权重本身。第二是不规则访存:稀疏矩阵的非零元素位置不固定,导致 CPU 无法利用顺序访存的预取机制。在无 Cache 的 MCU 上,这会造成 Flash 读取效率下降。第三是分支预测惩罚:稀疏解码循环中的 if (value != 0) 判断,在 MCU 上通常没有分支预测器,每次跳转都会造成流水线气泡。第四是并行度缺失:MCU 无法像 GPU 那样通过 warp 内掩码处理稀疏性,所有跳过操作都是串行的。

针对这些挑战,工程上常用的优化手段包括:使用 8 位索引减少索引存储;将稀疏矩阵按固定块(如 4x4)分块,块内使用位图标记非零位置;将稀疏解码循环展开 2~4 次,减少分支判断次数;将权重和索引交错存储,提高 Flash 顺序读取效率。

// 示例:Cortex-M4 上 CSR 稀疏矩阵向量乘的优化思路
void csr_spmv_int8(const int8_t *values, const uint16_t *col_idx,
                   const uint16_t *row_ptr, const int8_t *x,
                   int32_t *y, int num_rows) {
    for (int i = 0; i < num_rows; i++) {
        int32_t sum = 0;
        int start = row_ptr[i];
        int end = row_ptr[i + 1];
        // 循环展开 2 次,减少分支判断
        int j = start;
        for (; j + 1 < end; j += 2) {
            sum += (int32_t)values[j] * x[col_idx[j]];
            sum += (int32_t)values[j + 1] * x[col_idx[j + 1]];
        }
        for (; j < end; j++) {
            sum += (int32_t)values[j] * x[col_idx[j]];
        }
        y[i] = sum;
    }
}

3.5 案例拆解:Cortex-M4 上的稀疏全连接层部署

以某智能语音关键词识别(KWS)模型为例,其最后一个全连接层权重为 128x64,原始密集存储需要 128×64×4 = 32KB(float32)。经过剪枝后稀疏度达到 85%,但直接使用 CSR 格式后,实测推理延迟反而增加了 12%。分析发现,问题出在索引类型和访存模式上。

初始现象与数据:

  • 密集推理延迟:2.3ms(使用 CMSIS-DSP 的矩阵乘函数)
  • CSR 稀疏推理延迟:2.6ms(自实现循环)
  • Flash 占用:密集 32KB → CSR 6.8KB(值)+ 3.2KB(索引)= 10KB

分析过程:通过 profiler 发现,CSR 版本中 62% 的周期消耗在列索引读取和分支判断上。由于列索引使用 uint16_t,每次读取需要 2 次 Flash 访问(MCU 按 16 位读取),且索引值不连续,导致 Flash 预取失败率高达 40%。

优化操作:

  1. 将列索引改为 uint8_t(因为该层输入维度为 64,索引范围 0~63),索引存储减半。
  2. 将权重值和列索引交错存储为“值-索引”对,使 Flash 顺序读取。
  3. 使用 CMSIS-DSP 的 arm_mult_q15 函数替代手动乘法,利用硬件 MAC 指令。

结果对比:

方案 Flash 占用 推理延迟 相对密集加速
密集 float32 32KB 2.3ms 1.0x
CSR uint16 索引 10KB 2.6ms 0.88x(变慢)
CSR uint8 索引 + 交错存储 6.4KB 1.4ms 1.64x

复盘经验:稀疏格式的收益高度依赖硬件特性。在 MCU 上,索引宽度、存储布局、Flash 预取行为对性能的影响远大于算法复杂度。部署前应使用 cycle counter 进行逐段 profiling,而非仅依赖理论 MAC 减少量。

3.6 常见误区与设计建议

常见误区:

  • 误区一:认为稀疏度越高越好。实际上,当稀疏度超过 95% 时,索引开销和循环开销可能占据主导,收益增长趋缓甚至下降。
  • 误区二:忽略 Flash 随机访问惩罚。在无 Cache 的 MCU 上,随机访问 Flash 的代价可能比计算本身更高。
  • 误区三:直接移植桌面端稀疏库。Eigen、Intel MKL 的稀疏实现依赖 Cache 和 SIMD,在 MCU 上无法发挥效果。
  • 误区四:未考虑权重重排。通过按行/列重排权重,可以提高空间局部性,但很多开发者忽略这一步。

设计建议:在 MCU 上部署稀疏模型时,应遵循以下流程:首先,评估目标 MCU 的 SRAM/Flash 容量和访存特性;其次,对训练后的模型进行结构化剪枝(如通道剪枝或块剪枝),避免非结构化稀疏带来的索引开销;然后,选择与硬件匹配的稀疏格式(小矩阵用位图,大矩阵用 CSR);最后,使用 cycle counter 进行实测,对比密集与稀疏实现的延迟、功耗和内存占用。只有经过实测验证的稀疏方案,才能在 TinyML 场景中真正发挥价值。