执行模型
GPU 以步调一致的线程组(warp)为单位运行数千个线程,这些线程组被组织成块,并调度到流式多处理器上。同一个 warp 中的线程共享一条指令流,因此分支发散会导致串行执行、浪费通道。让每个 SM 上驻留足够多的 warp(即 occupancy),正是掩盖内存延迟的手段。
服务栈之下的那一层。模型的每一个算子最终都以 GPU 内核的形式运行,而内核质量决定了你买下的硬件究竟被用上了多少。你很少需要自己写内核,但读懂这一层,就能明白它之上的系统为何是那样运作的。
GPU 以步调一致的线程组(warp)为单位运行数千个线程,这些线程组被组织成块,并调度到流式多处理器上。同一个 warp 中的线程共享一条指令流,因此分支发散会导致串行执行、浪费通道。让每个 SM 上驻留足够多的 warp(即 occupancy),正是掩盖内存延迟的手段。
先是寄存器,然后是片上小而快的共享内存与各级缓存,再往外是片外的大容量 HBM。片上与片外的带宽差距有数量级之多,因此内核性能主要取决于数据跨越这条边界的次数有多少,以及访存合并得如何。
专用的矩阵乘累加单元贡献了现代 GPU 的大部分 FLOPS,工作在较低精度上(FP16/BF16、FP8、INT8)。喂不饱 Tensor Core 的内核,或者使用了硬件不具备的精度的内核,会让芯片的大部分处于空转。
每个内核都会先在算术吞吐或内存带宽中的某一项上触顶。LLM 的 prefill 偏重算力,decode 偏重带宽。知道某个算子落在这条线的哪一侧,就知道该增加 FLOPS 还是该减少数据搬运。
内核层面的工作与系统层面的工作互为补充:continuous batching 或 prefix caching 改变的是“要做哪些计算”,内核改变的是“每份计算跑得多快”。前者参见服务优化。
标准注意力会在 HBM 中实体化一个 N x N 的分数矩阵,于是内存流量随序列长度呈平方增长,运算也因此变成带宽受限。FlashAttention 重构了这个计算:它把 query、key、value 三个矩阵分块流经片上内存,随着分块到达增量地维护 softmax 归一化,并且完全不写出完整的分数矩阵。数学结果相同,内存流量却只有原来的一小部分,正是这一点让长上下文服务变得可行。
后续版本针对每一代硬件重新做了调优(更好的任务划分、异步的 Tensor Core 流水线、低精度路径),如今这项技术已内置于 PyTorch 和各主流服务引擎。其中的经验可以推广:内核层面最大的收益来自围绕内存层级重构计算,而不是对指令做微调。
按控制力和成本递增排列。请从最上层开始;只有当性能分析证明上一层不够用时,才往下走。
决策依据包括:你的算子有多标准、极致性能值多少钱、必须支持哪些硬件,以及团队的强项是编译器还是内核。请先做性能分析;瓶颈往往不在直觉指向的地方。