一句话定义:内核启动开销(Kernel Launch Overhead)是指 CPU 让 GPU 开始执行一个内核(kernel,即一段在 GPU 上并行运行的函数)时,完成整套“下命令、送命令、取命令、派线程”流程所花的固定时间。这笔时间与这个内核实际要算多少数据基本无关。
这笔开销花在哪了
一次 kernel 启动,背后是一串不短的流水线:应用调用运行时接口 → 驱动检查并打包参数(指针、网格维度、共享内存大小等)→ 把命令写进命令缓冲区 → 通过总线或队列告知 GPU → GPU 前端调度器读出命令 → 为线程块(block)分配寄存器和共享内存、把它们派发到各个流式多处理器(SM)上。哪怕这个 kernel 只做一次加法,这一串流程也得走完,量级通常在微秒级。单看很小,但架不住次数多。
打个比方:寄快递。寄一张纸和寄一箱书,下单、填单、揽收、扫描的流程一样长。如果你有一千件小东西要寄,却坚持一件一件单独下单,那你大部分时间都花在流程上,而不是“运输”本身。
和相邻概念的区别
| 概念 | 衡量的是什么 | 随数据量增长吗 |
|---|---|---|
| 内核启动开销 | 让 GPU“开始干活”的固定成本 | 基本不变 |
| 内核执行时间 | GPU 真正做计算的时间 | 随数据量增长 |
| 主机-设备拷贝开销 | 数据在 CPU 内存与显存之间搬运 | 随数据量增长,也随 PCIe 带宽变化 |
| CPU 线程上下文切换 | 操作系统换线程 | 与 GPU 无关 |
关键差别在“固定”二字:执行时间可以靠加大 batch 摊薄,启动开销不能,它只跟“启动了多少次”有关。
对从业者的意义
现代模型的计算图里常有成百上千个小算子:逐元素加法、偏置、激活函数、归一化,拆得很碎。每个都算得飞快,但每个都要启动一次,于是出现“启动受限”(launch-bound)的情况:GPU 利用率上不去,时间都耗在 kernel 之间的空隙里。用性能分析工具观察时间轴,会看到密密麻麻的小块和它们之间的空白,那块空白就是证据(具体工具行为以官方文档为准)。
常见的应对思路:
- 算子融合(operator fusion):把几个小算子合成一个 kernel,一次启动干几件事;
- AI 词典:CUDA Graph">CUDA Graph 之类的机制:把一串启动录制下来整体重放,省掉重复的 CPU 侧提交成本;
- 加大 batch 或合并小任务:让每次启动都有足够多的活可干;
- 减少不必要的同步:每次同步都可能打断流水,放大开销。
对普通人的意义
这解释了为什么“模型明明很小,跑起来却不快”。在手机端实时推理、语音唤醒、相机特效这类场景里,端到端延迟中有一部分不是“算”掉的,是“派活”派掉的。理解了这一点,就理解了为什么工程优化里“少启动几次”有时比“算得更快”更值钱。
