3、效率评估指标:编码速度、CPU/GPU利用率、内存占用、编码延迟

聊完质量,咱们得聊聊「效率」。说白了,就是你的机器为了转出这个视频,到底付出了多少代价。

我见过不少团队,一上来就追求最高画质,结果一条4K片子转了一天一夜。客户问「好了没?」你只能说「还在跑...」这种尴尬,我经历过太多次了。

所以这一章,咱们把效率拆开来看。四个核心指标:编码速度、CPU/GPU利用率、内存占用、编码延迟。你把这四个吃透了,调优才有方向。

3.1 编码速度:到底有多快?

编码速度最直观。FFmpeg里有两个常用参数:-benchmark 和 -benchmark_all。

ffmpeg -i input.mp4 -c:v libx264 -preset medium -benchmark output.mp4

跑完之后,你会看到类似这样的输出:

bench: utime=125.3s stime=8.7s rtime=134.2s
bench: maxrss=142352kB

这里 rtime 就是实际耗时(real time)。utime 是用户态CPU时间,stime 是内核态CPU时间。如果 rtime 远小于 utime+stime,说明你的CPU多核利用率很高——这是好事。

我个人习惯用 fps(每秒帧数) 来衡量速度。FFmpeg默认会打印fps信息:

frame= 1234 fps= 45 q=28.0 size=    5120kB time=00:00:41.13 bitrate=1019.3kbits/s speed=1.51x

这里的 speed=1.51x 表示编码速度是实时播放速度的1.51倍。如果speed小于1,说明你的机器跑不动这个转码任务——嗯,该升级硬件或者降低参数了。

我的习惯: 做对比测试时,固定用 -benchmark 记录rtime,同时看fps。两个数据互相印证,不容易被单次波动误导。

3.2 CPU/GPU利用率:资源有没有吃满?

光看速度还不够。你得知道资源用到了什么程度。

CPU利用率,我一般用 top 或 htop 实时看。FFmpeg是多线程的,如果CPU利用率只有30%-40%,说明有瓶颈——可能是磁盘I/O,可能是内存带宽,也可能是编码器本身没开够线程。

举个例子:

ffmpeg -i input.mp4 -c:v libx264 -preset ultrafast -threads 4 output.mp4

-threads 控制线程数。默认是auto,但auto不一定最优。我在项目中遇到过,某些老版本FFmpeg在auto模式下只开了1个线程,白白浪费了16核的机器。后来我改成 -threads 8,速度直接翻倍。

GPU利用率呢?用 nvidia-smi 看:

nvidia-smi -l 1

重点关注 GPU-Util 和 Encoder/Decoder Utilization。如果GPU-Util只有20%,但Encoder Util已经99%,说明编码器单元是瓶颈。这时候加更多GPU核心也没用,得换更强的编码器或者降低编码复杂度。

注意: GPU转码时,CPU和GPU是协同工作的。如果CPU利用率过高(比如超过50%),反而可能拖累GPU。我曾经调试一个H.265转码任务,发现CPU在拼命做预处理,GPU反而在空等。后来把部分预处理移到GPU上,整体速度提升了30%。

3.3 内存占用:别让内存成为瓶颈

内存占用容易被忽略。但一旦爆了,系统开始用swap,速度直接崩盘。

FFmpeg的 -benchmark 会输出 maxrss,就是最大常驻内存集。单位是kB。比如 maxrss=142352kB 就是大约139MB。

哪些操作会吃内存?

  • 高分辨率+高比特率:4K、8K视频,每帧数据量大,内存占用自然高。
  • 长GOP:参考帧多,需要缓存更多帧。
  • 多路并行转码:每个进程独立占内存,叠加起来很可观。
  • 复杂滤镜链:比如多个 scale、overlay、fps 串联,每个滤镜都可能缓存帧。

我建议你在生产环境里,给FFmpeg进程预留至少2倍于 maxrss 的内存空间。为什么?因为 maxrss 是峰值,但系统还需要给操作系统、其他服务留余量。

经验值: 1080p H.264转码,-preset medium,内存占用通常在200-400MB。4K H.265转码,-preset slow,可能飙到1.5-3GB。如果你用 libx265 的 -x265-params pools=... 开大量线程,内存占用还会更高。

3.4 编码延迟:实时场景的生命线

编码延迟,指的是从输入一帧到输出这一帧的编码结果,所花的时间。对于直播、视频会议、远程桌面这些场景,延迟是硬指标。

FFmpeg里可以用 -tune zerolatency 来优化:

ffmpeg -i input.mp4 -c:v libx264 -tune zerolatency -preset ultrafast output.mp4

但代价是什么?码率会飙升,画质会下降。说白了,就是用空间换时间。

延迟的测量,我一般用 -progress 参数输出时间戳,然后自己算差值:

ffmpeg -i input.mp4 -c:v libx264 -tune zerolatency -progress pipe:1 output.mp4 2>&1 | grep "out_time="

或者更直接,用 ffmpeg 的 -stats_period 控制日志输出频率,观察每一帧的处理时间。

影响延迟的关键因素:

  • GOP大小:GOP越大,B帧越多,延迟越高。zerolatency模式下通常只用IDR帧和P帧,不用B帧。
  • Lookahead:某些编码器(如x265)的lookahead功能会提前分析多帧,增加延迟。实时场景建议关闭。
  • 线程同步:多线程编码时,帧之间的依赖关系会导致等待。 -tune zerolatency 会限制线程间的同步开销。
我曾经踩过的坑: 有一次做低延迟直播,用了 -tune zerolatency 但延迟还是高。查了半天,发现是音频编码器 libfdk_aac 的帧大小设成了1024,导致音频缓冲延迟叠加。换成 -frame_size 256 后,延迟从800ms降到了200ms。所以,延迟优化要音视频一起看。

3.5 四个指标的权衡关系

这四个指标不是孤立的。它们之间互相影响,甚至互相矛盾。我画了一张图,帮你理清关系:

效率评估指标关系图 效率评估指标关系图 编码效率 编码速度 CPU/GPU利用率 内存占用 编码延迟 权衡:速度↑ → 延迟↓ → 质量↓ → 内存↑ → 利用率↑ → 速度↓ 实际场景优先级: • 离线批量转码:速度 > 利用率 > 内存 > 延迟 • 直播/实时转码:延迟 > 速度 > 利用率 > 内存 • 嵌入式/低配设备:内存 > 利用率 > 速度 > 延迟

从图上你能看到,这四个指标形成了一个闭环。你优化其中一个,往往会影响到另外几个。比如:

  • 提高编码速度(用ultrafast),CPU利用率下降,内存占用减少,延迟降低,但画质和码率会变差。
  • 降低延迟(zerolatency),编码速度可能下降(因为限制了B帧和lookahead),CPU利用率反而可能升高(因为每帧都要独立处理)。
  • 增加线程数,CPU利用率上升,编码速度提升,但内存占用也会增加(每个线程需要独立的缓冲区)。
我的建议: 不要试图同时优化所有指标。先明确你的场景优先级。比如离线转码,我优先看速度和内存;直播转码,我死磕延迟和CPU利用率。定好优先级,再针对性地调参。

3.6 实战:如何用FFmpeg收集这些指标

最后,给你一个我常用的「效率评估三板斧」脚本。每次调参前,先跑一遍这个,拿到基线数据:

#!/bin/bash
# 效率评估脚本 - 输出编码速度、CPU利用率、内存占用、延迟

INPUT="input.mp4"
OUTPUT="output.mp4"
PRESET="medium"

# 1. 编码速度 + 内存占用
echo "=== 编码速度 & 内存占用 ==="
ffmpeg -i $INPUT -c:v libx264 -preset $PRESET -benchmark -y $OUTPUT 2>&1 | grep "bench:"

# 2. CPU利用率(后台运行,同时用top采样)
echo "=== CPU利用率(采样5秒) ==="
ffmpeg -i $INPUT -c:v libx264 -preset $PRESET -y $OUTPUT &
PID=$!
top -b -n 5 -p $PID | tail -n 10 | awk '{print $9}' | paste -sd+ | bc
wait $PID

# 3. 编码延迟(用zerolatency对比)
echo "=== 编码延迟(zerolatency) ==="
ffmpeg -i $INPUT -c:v libx264 -tune zerolatency -preset ultrafast -progress pipe:1 -y output_zl.mp4 2>&1 | grep "out_time=" | tail -n 1

这个脚本虽然简陋,但够用。你可以在自己的机器上跑一遍,看看不同preset下四个指标的变化。数据会说话。

注意: 单次测试可能有波动。我建议每个参数组合跑3次,取中位数。另外,机器负载会影响结果——测试时尽量关掉其他高负载进程。