训练性能与部署效率间的差距促使人们需要专用编译。不过,要实现有针对性的优化,弄清推理过程中效率低下的具体来源是不可或缺的。机器学习推理工作负载,尽管通常执行预训练模型,但与训练相比,它们呈现出独特的性能特点和瓶颈。这些瓶颈通常归为三类主要问题:计算限制、内存带宽限制和延迟开销。识别在给定场景下哪种因素占据主导,能确定最有效的优化策略。计算受限操作当执行时间主要受硬件算术单元(例如ALU、FPU、专用矩阵乘法器)的处理能力限制时,该推理工作负载被认为是计算受限的。这通常发生于算术密度高的操作,即每访问一字节数据时执行大量计算。密集矩阵乘法: 全连接层和循环神经网络(RNN)的根本组成部分,这些操作涉及大量的乘加(MAC)运算。现代处理器(CPU、GPU、加速器)为此类运算设有专用高吞吐量单元,但要达到峰值性能,需要仔细生成代码以最大化单元利用率、管理数据局部性并运用向量/矩阵指令(SIMD、Tensor Cores等)。如果编译器未能将计算有效映射到这些单元,性能就会下降。卷积: 在卷积神经网络(CNN)中占据主要地位,卷积也具有高算术密度,尤其是在特征图较大或通道数很多的情况下。与矩阵乘法类似,优化卷积在很大程度上依赖于将计算高效映射到硬件架构。像Winograd或基于FFT的卷积等方法可以减少原始操作计数,但会引入不同的数据访问模式和潜在开销。在卷积执行期间未能使计算单元饱和会导致计算瓶颈。硬件利用率: 即使有足够的理论FLOPS/TOPS,低效的代码也可能导致可用计算资源未被充分使用。这可能源于指令调度不当、未能充分发挥指令级并行性(ILP),或未能有效运用专用向量/矩阵单元。通用编译器可能难以针对机器学习中高度专业的计算模式生成最佳代码。如果增加原始处理能力(例如更高的时钟速度、更多的计算单元)直接导致执行时间成比例减少,并且假设内存和延迟不是限制因素,则工作负载是计算受限的。内存受限操作许多机器学习操作,特别是在推理时批量较小的情况下,是内存受限的。这意味着执行时间主要由数据在内存层级之间(例如DRAM到缓存、不同缓存层级、主机到设备内存)传输所花费的时间决定,而不是计算本身。“内存墙”,即处理器速度与内存速度之间日益扩大的差异,是这里一个重要的考量因素。数据传输开销: 每一个移动的字节都消耗时间和能量。访问大型张量(例如嵌入、大型激活)的操作,或算术密度低的操作(如ReLU、加法、融合后的批归一化等逐元素操作)很快就会受内存带宽限制。主机CPU与加速器(GPU、TPU)之间的数据传输常因互连速度相对较慢(如PCIe)而成为主要瓶颈。缓存效率低下: 糟糕的数据局部性导致频繁的缓存未命中,迫使处理器等待来自较慢内存层级的数据。张量数据布局(例如NCHW与NHWC)对卷积等操作的空间和时间局部性有显著影响。编译器优化,例如循环分块、数据布局转换和软件预取,旨在提高缓存利用率,但次优选择很容易导致内存受限执行。带宽饱和: 硬件具有有限的内存带宽。如果操作所需数据的速率超过此限制,计算单元将停滞,等待数据。这在处理大量数据但每个元素计算量相对较少的操作中很常见。请考虑以下简化的屋脊线模型图示,它显示了基于硬件能力的性能边界。落在倾斜部分的操作通常是内存受限的,而达到水平上限的操作则是计算受限的。{"data": [{"x": [0.01, 0.1, 1, 10, 100], "y": [0.2, 2, 20, 200, 200], "mode": "lines", "name": "内存带宽限制 (20 GB/s)", "line": {"color": "#4263eb"}}, {"x": [0.01, 100], "y": [200, 200], "mode": "lines", "name": "峰值计算能力 (200 GFLOPS)", "line": {"color": "#f03e3e"}}, {"x": [0.05, 5], "y": [1, 100], "mode": "markers", "name": "示例操作", "marker": {"color": "#12b886", "size": 8}}], "layout": {"title": "屋脊线模型", "xaxis": {"title": "算术密度 (FLOPs/字节)", "type": "log"}, "yaxis": {"title": "性能 (GFLOPS)", "type": "log"}, "legend": {"yanchor": "bottom", "y": 0.01, "xanchor": "left", "x": 0.01}}}屋脊线模型,它表示基于计算能力(水平线)和内存带宽(倾斜线)与操作算术密度(点)的性能限制。“屋顶”下方的操作受到内存带宽或计算峰值的限制。优化内存受限工作负载包括最小化数据移动,通过算子融合和布局转换等方法提升数据局部性,以及细致管理内存分配和传输。延迟受限场景“延迟”指的是单个推理请求从输入到输出的总耗时。尽管通常需要高吞吐量(每秒推理次数),但许多应用(例如实时目标检测、语音助手)对延迟非常敏感。此处的瓶颈不仅源于计算或内存速度,还来自开销和顺序依赖。单批量推理: 以单批量大小进行推理通常会显现出延迟瓶颈。硬件加速受益于大批量中固有的并行性;小批量可能无法充分利用计算单元或有效掩盖内存访问延迟。内核启动开销: 在GPU等加速器上,启动单个计算内核会产生开销。一个由许多小型、顺序操作组成的模型,其大部分时间可能消耗在启动延迟上,而非有用的计算。算子融合是一种重要的方法,通过将多个操作合并为更少、更大的内核来减轻此问题。框架和运行时开销: 机器学习框架(例如TensorFlow、PyTorch)以及底层的运行时系统在操作调度、内存管理、设备间同步和处理动态输入方面会引入各自的开销。在高度优化的场景中,这些开销可能变得明显,特别是对于单个算子执行时间非常快的模型。流水线停滞: 执行图中的操作之间存在依赖关系,如果一个阶段等待前一个较慢阶段的输出,则可能导致停滞。异构系统(CPU + GPU)中糟糕的调度或负载均衡会加剧此问题。延迟优化通常需要与吞吐量优化不同的策略,主要侧重于减少顺序依赖、最小化框架开销、激进的融合,并可能使用JIT编译等编译技术来降低频繁执行代码路径的调度成本。弄清机器学习推理工作负载是主要受计算限制、内存限制还是对延迟敏感,是应用本课程中讨论的高级编译器和运行时优化的第一步。通常,一个工作负载会展现出这些特点的混合情况,因此需要一种平衡的优化方法,并针对特定的模型架构、硬件目标和部署要求进行定制。