算子与 Kernel
从数学定义走到布局、融合与硬件执行路径
建议阅读:约 18 分钟
学习目标
本章关键词
| 关键词 | 解释 | ESP32 工程类比 |
|---|---|---|
| Kernel | 针对具体硬件实现一个算子的低层计算代码。 | 像同一外设 API 的芯片专用驱动。 |
| Layout | 张量维度在内存中的排列方式,如 NCHW 或 NHWC。 | 像结构体字段和对齐规则。 |
| Fusion | 将多个连续算子合并为一次执行。 | 像把多个小 DMA 事务合并以减少中断和搬运。 |
| Tiling | 把大计算分块以适配寄存器或 cache。 | 像用分块 buffer 处理长数据流。 |
承上:回顾与定位
上一章把浮点值映射到低比特表示;本章追踪这些值如何被算子、布局与 kernel 真正执行。
历史发展脉络
算子的历史有两条交织路线:一条发明能表达任务的网络结构,另一条把这些数学结构变成可高效执行的 kernel。卷积网络从层级感受野走到端到端训练后,GPU 原语库把优化实现与框架分开,编译器又把融合、布局和调度自动化。现代 runtime 的职责正位于数学图与异构硬件之间。
Neocognitron 展示层级局部感受野与位移鲁棒性
Fukushima 的 Neocognitron 以层级结构处理视觉模式,并追求位置变化下的识别。它不是今天以反向传播训练的 Conv kernel,却把局部连接、特征层级和空间复用带入了卷积网络谱系。
Neocognitron 原始论文 ↗LeNet 把卷积、下采样与梯度训练连成应用系统
LeCun 等人的文档识别工作系统化展示了卷积网络及端到端梯度学习。算子不再是孤立公式,而是与输入几何、权重共享和任务后处理共同构成可运行流水线。
LeNet 文档识别原始论文 ↗cuDNN 将深度学习原语做成可复用高性能库
cuDNN 提供面向 GPU 的卷积等优化原语,使框架不必随每代并行硬件重写全部 kernel。论文把深度学习算子库类比 BLAS,并展示算法选择、内存使用与硬件优化可以隐藏在稳定接口之后。
cuDNN 原始论文 ↗TVM 把图融合与低层调度纳入统一编译搜索
TVM 同时处理高层算子融合、硬件原语映射与内存延迟隐藏,并用成本模型搜索低层优化。kernel 选择从厂商手写库扩展到可针对 CPU、GPU、FPGA 和加速器生成的调度空间。
TVM OSDI 原始论文 ↗Execution Provider 让同一图按能力切给异构后端
ONNX Runtime 以 Execution Provider 接入不同硬件实现,并把支持的节点或子图分配给相应后端。现代部署因此不只问“有没有 NPU”,还要检查领取了哪些节点、边界搬运多少数据以及哪些路径回退 CPU。
ONNX Runtime Execution Providers 官方文档 ↗类比图解
同一份菜谱如何落到一间拥挤的专业厨房
算子像菜谱中的“切丁、翻炒、收汁”,只规定输入、动作和成品;kernel 是某位厨师在特定灶台上的具体手法。食材按取用顺序摆盘是 layout,把切丁和腌制合在同一案板上是 fusion,把一大锅拆成刚好放进炒锅的小份则是 tiling。菜谱没变,出餐速度可以相差数倍。
类比的边界: 厨房类比不能表达并行线程、向量指令和浮点舍入,也容易让人误以为融合总是有利;真实 fusion 会受量化尺度、分支复用和 backend 支持限制,数值等价与性能收益都必须用目标硬件验证。
本章讲解
算子契约包含属性、边界与数值约定
Conv 不只是一条求和公式,还包括 stride、padding、dilation、groups、权重维度顺序和输出 shape 规则;Softmax 还必须指定归一化 axis 与稳定计算方式。两个 runtime 都声称支持同名算子,也可能只覆盖部分 dtype 或属性组合。模型接入时应从真实节点导出契约表,并用非对称 shape 与边界输入验证。
同一 Conv 可以对应多种算法与 workspace 交换
直接卷积、im2col+GEMM、Winograd 或硬件专用路径具有不同适用区间。im2col 便于复用成熟矩阵乘 kernel,却可能展开出大 buffer;Winograd 可减少部分乘法,但对尺寸、数值精度和变换开销敏感。backend 的算法选择必须连同实际 shape、可用 workspace 和量化模式测量,不能只比较理论 MAC。
Layout 决定相邻数据是谁,也决定向量单元吃得顺不顺
NCHW 与 NHWC 改变通道和空间维在内存中的邻接关系;即使维度名称一致,stride 不连续也可能触发隐式 copy。某个 kernel 偏好通道连续,另一个加速器却要求块化布局,边界转换便会整张读写激活。profile 时应把 transpose、reorder 和 memcpy 当作正式节点计时,而非归入无法解释的框架开销。
Tiling 的目标是让重复使用发生在最快的存储层
GEMM 将 M、N、K 分块后,把小块输入和权重装入寄存器或 cache,多次 MAC 后才写回;tile 过大会溢出工作集,过小又增加循环与边界开销。SIMD 还要求对齐,并为尾部元素处理 mask 或标量路径。在 ESP32 上应测试真实地址、对齐和 DMA 来源,因为桌面数组的理想连续性不会自动出现。
融合与加速器分区要用“少落地几次”来验收
Conv、Bias、ReLU 融合可让中间值留在寄存器或片上 SRAM,但若某输出还被旁路节点消费,或相邻算子的量化尺度不兼容,融合可能受限。NPU 子图两侧同样会产生同步、布局变化和 copy。验收报告应列出融合前后节点、分区边界、搬运字节与端到端时间,避免用单个 kernel 加速比替代系统收益。
动态过程演示
Conv–BN–ReLU 如何落成一次分块执行
动画从抽象图逐层下钻到内存:先核对算子契约,再选择融合与布局,最后让 tile 在片上缓冲区中循环装载、计算和写回。
展开算子契约 · Conv(stride,pad,groups) → BN → ReLU
属性卡附着到节点,输入输出 shape 同步变化
同名算子只有属性、axis 与 dtype 都匹配才算语义兼容
循环 / 返回条件: tile 沿输出空间循环直到完成;切换 layout、tile 大小或 backend 时,动画重置计数器并并排保留上一轮的字节数、workspace 与延迟。
查看静态全景图
Graph: Conv → BN → ReLU Optimizer: Conv+BN+ReLU fusion Kernel: tile → load → MAC → store
代码或命令示例
for (m_tile : M)
for (n_tile : N)
acc = 0
for (k_tile : K) acc += A * B
C = acc + bias动手实验
实验记录与导出
工程陷阱
核心测试
完成 3 道单选题后提交;提交前不会显示答案。
在 GitHub 上讨论本章
登录 GitHub 后提问、补充实测或分享你的实现;评论会保存在本课程的 GitHub Discussions 中。
评论区需要 JavaScript;也可以直接打开 GitHub 讨论区: 打开 GitHub 讨论区 ↗
延伸阅读
启下:下一章如何使用本章能力
下一章把数据、训练、转换、量化与固件 runtime 串成非 LLM 端侧部署闭环。
下一天: Day 5 · 端侧模型部署