1. 算子优化概述

大家好,我是这门课的主讲人。在AI芯片和编译器领域摸爬滚打了十来年,今天咱们来聊聊算子优化这件事。

说实话,我刚入行那会儿,觉得算子就是个黑盒子——输入数据进去,输出结果出来,完事。后来踩了不少坑才明白,算子的实现质量,直接决定了整个AI模型的训练和推理速度。你想想看,一个ResNet-50里上百个算子,每个快10%,整体就是几倍的差距。

什么是算子

算子,英文叫Operator,简称Op。说白了,它就是一个数学运算的基本单元。

在AI框架里,算子就是构建神经网络的积木。卷积、矩阵乘、ReLU、Softmax、BatchNorm……这些都是算子。每个算子接收输入张量(Tensor),经过计算,输出新的张量。

核心理解:算子 = 计算逻辑 + 数据流。它定义了「输入长什么样,输出怎么算出来」。

举个例子,一个简单的加法算子:

// 伪代码:向量加法算子
void vector_add(float* a, float* b, float* c, int n) {
    for (int i = 0; i < n; i++) {
        c[i] = a[i] + b[i];
    }
}

嗯,看起来很简单对吧?但实际生产环境里的算子要考虑内存布局、并行策略、精度控制……远没这么轻松。

算子在AI框架中的角色

AI框架(比如PyTorch、TensorFlow、MindSpore)本质上就是个算子调度系统。用户写一个模型,框架把它拆成一张计算图,图中的每个节点就是一个算子。

我习惯把AI框架比作一个餐厅:

  • 计算图 = 菜单(告诉厨房要做什么菜)
  • 算子 = 厨师(每个厨师负责一道菜)
  • 张量 = 食材(在厨师之间传递)

算子就是那个真正干活的角色。框架负责调度,算子负责执行。所以算子的性能,直接决定了整个模型的执行效率。

我的经验:在优化一个模型时,先做profiling找到热点算子。通常20%的算子消耗了80%的计算时间。把这20%优化好,效果立竿见影。

为什么需要算子优化

你可能会问:框架不是已经实现了算子吗?直接用不就行了?

问题在于,通用实现往往不是最优实现。框架提供的算子要考虑各种输入形状、数据类型、硬件平台,所以它必须做很多通用化处理。这就导致:

  • 通用算子可能没利用到硬件的特殊指令(比如Tensor Core、AMX)
  • 通用算子的内存访问模式可能不是最优的
  • 通用算子可能没做算子融合,导致多次kernel launch开销

我曾经接手过一个项目,模型在GPU上跑,Conv算子的利用率只有30%。后来通过手写cuDNN替代方案,把利用率提到了85%以上。这就是算子优化的价值。

注意:不是所有算子都值得优化。如果一个算子只占整体时间的1%,花一周去优化它,性价比太低了。先找热点,再动手。

算子优化的核心目标

算子优化说到底,就三个目标:算得快、省内存、用满带宽。

目标 含义 典型手段
计算加速 减少计算时间,提高吞吐 向量化、并行化、算子融合、利用特殊指令
内存优化 减少内存占用,避免OOM in-place操作、内存复用、算子融合减少中间结果
带宽利用 提高数据搬运效率 合并访存、数据布局优化、缓存友好

这三个目标有时候是矛盾的。比如为了计算加速,你可能需要更多中间结果,这就增加了内存占用。所以算子优化本质上是个权衡的艺术。

计算加速

计算加速的核心思路就两条:减少计算量和让计算更快。

减少计算量,比如用Winograd算法做卷积,把乘法次数降下来。让计算更快,比如用SIMD指令一次处理多个数据,或者把计算切分到多个核上并行执行。

我印象很深的一个案例:优化一个LayerNorm算子。原始实现是逐元素计算均值和方差,需要两次遍历。改成一次遍历后,速度直接翻倍。有时候优化思路就这么简单。

内存优化

内存优化在训练场景尤其重要。模型越来越大,显存越来越不够用。

常用的手段包括:

  • In-place操作:输出直接覆盖输入,省掉一份内存
  • 算子融合:把多个小算子合并成一个,减少中间张量
  • 内存池:预分配大块内存,避免频繁malloc/free

举个例子,Conv + BN + ReLU这三个算子,如果分开执行,中间会产生两个临时张量。融合成一个算子后,中间结果只在寄存器里传递,内存占用大幅降低。

带宽利用

这个点容易被忽视,但其实是很多算子的瓶颈。尤其是带宽密集型算子(比如Element-wise操作),计算本身很快,数据搬运反而成了短板。

提高带宽利用的核心是让数据在内存中连续存放,这样CPU/GPU可以一次搬一大块数据,而不是东搬一点西搬一点。

我曾经优化过一个Gather算子,原始实现是按索引逐个读取数据,带宽利用率不到10%。改成批量读取+向量化后,利用率到了70%以上。说白了,就是让硬件干活更「顺手」。

避坑指南:我曾经在优化一个矩阵乘算子时,只顾着调计算逻辑,忽略了数据对齐。结果在ARM平台上性能反而下降了。后来发现是未对齐访问触发了异常处理。嗯,从那以后我每次写算子都会检查数据对齐。

本章知识体系

下面这张图总结了算子优化的核心知识框架,你可以对照着梳理自己的理解:

算子优化知识体系 算子优化 计算加速 内存优化 带宽利用 向量化/SIMD 并行计算 算子融合 In-place操作 内存复用 减少中间张量 合并访存 数据布局优化 缓存友好 三者权衡,没有银弹

这张图把算子优化的核心脉络理清楚了。你会发现,所有优化手段最终都指向这三个目标中的一个或多个。后面的章节,我们会逐一深入每个方向,从理论到代码,一步步带你掌握算子优化的实战技巧。

好了,第一章就到这里。记住一句话:算子优化不是炫技,而是让硬件干它最擅长的事。下一章我们聊聊「计算加速」的具体手段,到时候见。

本章要点回顾:

  • 算子是AI框架的基本计算单元,决定了模型性能
  • 算子优化的三个核心目标:计算加速、内存优化、带宽利用
  • 优化前先做profiling,找到热点再动手
  • 三个目标需要权衡,没有万能方案

想要更多资料,联系可可