一、算子性能全景概览

大家好,我是老张。今天咱们聊聊算子性能优化这件事。

说实话,我入行那会儿,大家写算子就是「能跑就行」。后来模型越来越大,数据越来越多,性能问题才真正浮出水面。我记得第一次被老板叫去优化一个卷积算子,跑一次推理要 3 秒多,优化完降到 200 毫秒——那种成就感,嗯,至今难忘。

1.1 什么是算子

算子,说白了就是深度学习网络里的一个基本运算单元。你想想看,一个神经网络模型,无非就是卷积、池化、激活、矩阵乘法这些操作的组合。每个这样的操作,就是一个算子。

举个例子:

// 一个简单的 ReLU 算子
// 输入:x,输出:max(0, x)
for (int i = 0; i < n; i++) {
    y[i] = x[i] > 0 ? x[i] : 0;
}

我在项目中遇到过不少新人,一上来就写这种循环。嗯,代码没错,但性能嘛……咱们后面会讲怎么优化。

核心理解:算子 = 输入数据 + 计算逻辑 + 输出数据。优化算子,就是让这三者配合得更高效。

1.2 算子性能的衡量指标

怎么判断一个算子写得好不好?我个人习惯看三个指标:

延迟(Latency)

延迟就是一次运算从开始到结束的时间。单位通常是毫秒或微秒。你想想看,用户点一下按钮,等 5 秒才出结果,谁受得了?

我建议把延迟拆开看:

  • 计算延迟:CPU/GPU 真正在算的时间
  • 访存延迟:数据从内存搬到寄存器的时间
  • 调度延迟:线程/任务切换的时间

吞吐量(Throughput)

吞吐量是单位时间内能处理多少个请求。比如一个推理服务,每秒能处理 1000 张图片,那吞吐量就是 1000 QPS。

延迟和吞吐量是什么关系?我打个比方:

  • 延迟 = 做一道菜的时间
  • 吞吐量 = 一小时内能做多少道菜

你缩短做菜时间,吞吐量自然就上去了。但反过来,吞吐量高不一定延迟低——比如你同时开 10 个灶台,每道菜可能反而更慢。

算力利用率(Utilization)

这个指标很多人忽略,但其实很关键。算力利用率 = 实际计算量 / 理论峰值计算量。

举个例子:

硬件 理论峰值 实际达到 利用率
NVIDIA A100 312 TFLOPS 250 TFLOPS 80%
你的算子 312 TFLOPS 30 TFLOPS 9.6%

我曾经优化过一个算子,利用率只有 5%。说白了,硬件在「摸鱼」。后来通过调整数据排布和循环顺序,利用率提到了 60% 以上。

我的经验:先看利用率,再看延迟。利用率低,说明优化空间大;利用率已经很高了,那就得从算法层面想办法。

1.3 影响算子性能的关键因素

影响算子性能的因素很多,但归根结底就三个:计算、访存、调度。

计算(Compute)

计算就是 CPU/GPU 真正在「算」的那部分。比如矩阵乘法、卷积运算。

影响计算性能的因素:

  • 算法复杂度:O(n²) 和 O(n log n) 差很多
  • 指令集:AVX、NEON、Tensor Core 这些能用就用
  • 数据精度:FP32 和 FP16 的计算速度差一倍

我记得有一次优化一个矩阵乘法算子,用 AVX2 指令集重写后,速度提升了 4 倍。嗯,指令集的力量。

访存(Memory Access)

访存往往是最大的瓶颈。为什么?因为 CPU 算得越来越快,但内存速度没跟上。

你想想看:

  • CPU 一级缓存访问:1 纳秒
  • 内存访问:100 纳秒
  • 差了 100 倍!

所以优化访存,核心就是让数据尽量待在缓存里。我常用的方法:

  • 数据局部性:访问连续内存,别跳来跳去
  • 数据排布:NCHW 还是 NHWC?选对了差很多
  • 预取:提前告诉 CPU 要加载哪些数据

避坑指南:我曾经花了两周优化计算逻辑,结果发现瓶颈在访存。白忙活一场。所以先做 profiling,找到真正的瓶颈再动手。

调度(Scheduling)

调度就是怎么安排计算任务。多线程、多核、GPU 的 warp 调度,都属于调度范畴。

影响调度性能的因素:

  • 任务粒度:任务太大,负载不均;任务太小,调度开销大
  • 依赖关系:有依赖的任务只能串行
  • 资源竞争:多个线程抢同一个资源,性能就下降

我建议这样思考:

  • 先看能不能并行
  • 再看怎么分任务
  • 最后看怎么减少竞争

知识体系总览

下面这张图,是我自己总结的算子性能优化知识体系。你把它记在心里,后面每一章都会围绕它展开。

算子性能优化 计算 (Compute) 访存 (Memory) 调度 (Scheduling) 算法复杂度 指令集 (AVX/Tensor Core) 数据精度 (FP32/FP16) 数据局部性 数据排布 (NCHW/NHWC) 缓存友好 / 预取 任务粒度 依赖关系 资源竞争 延迟 (Latency) 吞吐量 (Throughput) 算力利用率 算子性能优化 = 计算优化 + 访存优化 + 调度优化

这张图展示了算子性能优化的核心框架。三个关键因素——计算、访存、调度——共同决定了算子的性能。而延迟、吞吐量、算力利用率,就是衡量优化效果的三把尺子。

后面的章节,我会逐一深入讲解每个因素怎么优化。嗯,咱们慢慢来。

我的建议:刚开始做算子优化,别想着一步到位。先跑 profiling,找到瓶颈,然后针对性地优化。一次只改一个地方,看看效果。这样积累经验,慢慢就有感觉了。

可可精品资料库