Профессиональные GPU в серверах позиционируются как устройства для высокопроизводительных вычислений, систем искусственного интеллекта и рендеринговых ферм для 3D-графики. Стоит ли их применять для энкодинга, или это стрельба из пушки по воробьям? Попробуем разобраться.
Для работы с многопоточным видео достаточно мощностей современных CPU и решений наподобие Intel Quick Sync. Более того, некоторые специалисты считают, будто загрузка профеcсиональных GPU декодингом и энкодингом — пустая трата ресурсов. Для потребительских видеокарт количество входящих потоков специально ограничивают до двух-трех, хотя мы уже убедились, что небольшое шаманство с драйвером позволяет это ограничение обойти. В предыдущей статье тестировались бытовые видеокарты, а сейчас мы займемся более серьезными — NVIDIA RTX A4000.
Подготовка к тестированию
Что делать, если вывод lscpu выдает вам что-то вроде AMD Ryzen 9 5950X 16-Core Processor, но в компьютер вставлена NVIDIA RTX A4000 с 16 ГБ оперативной памяти, а вы хотите перекодировать и записать поток с нескольких сетевых камер? Информация с них обычно поступает через http, rtp или rtsp, и наша задача — поймать эти потоки, перекодировать их в нужный формат и записать каждый в отдельный файл.
Для проверки мы в HOSTKEY создали небольшой тестовый стенд с указанной выше конфигурацией CPU/GPU без специальной оптимизации и 32 ГБ оперативной памяти. На нем через ffmpeg мы будем принимать мультикаст-вещание в форматах http и rtsp (использован видеофайл bbb_sunflower_1080p_30fps_normal.mp4 из деморепозитория Blender), декодировать его в разном количестве потоков ffmpeg и записывать каждый из них в отдельный файл. Как видно из названия, мы принимаем поток в формате 1080p (30 кадров в секунду). Энкодинг будет применяться только к видео, а звуковые потоки пойдут без изменения.
Также несущественно, берем мы один входящий поток и имитируем его мультипоточность или параллельно обрабатываем несколько потоков. Работа с сетью и текущие процессы на тестовом стенде отнимают менее 1% ресурсов CPU, поэтому можно считать, что основную нагрузку на процессор и дисковую подсистему даст именно энкодинг.

Все дальнейшее повествование будет вестись для вещания через http, поскольку результаты для потока rtsp оказались сравнимыми. Чтобы не плодить множество консолей терминала на сервере, для теста были созданы простые bash-скрипты, в которые при запуске передается требуемое количество инстансов ffmpeg, перекодирующих видеопоток в h264.
Энкодинг на голом CPU:
#!/bin/bash
for (( i=0; i<$1; i++ )) do
ffmpeg -i http://XXX.XXX.XXX.XXX:5454/ -an -vcodec h264 -y Output-File-$i.mp4 &
done
На GPU мы будем использовать возможности видеокарты через NVENC (как собрать ffmpeg с его поддержкой, мы рассказывали в первой статье цикла):
#!/bin/bash
for (( i=0; i<$1; i++ )) do
ffmpeg -i http://XXX.XXX.XXX.XXX:5454/ -an -vcodec h264_nvenc -y Output-File-$i.mp4 &
done
Скрипты запускают в цикле мультикаста и ловят в сети нашего кролика. Предварительно стоит проверить через тот же vlc или ffplay, что поток реально вещается. Результат мы будем оценивать по загрузке CPU/GPU, утилизации памяти и качеству записываемого видео, где главными для нас будут два параметра: fps (он должен быть стабильным и не опускаться ниже 30 кадров в секунду) и speed (показывает, успеваем ли мы обрабатывать видео на лету). Для realtime параметр speed должен быть больше 1.00x.
Проседания этих двух параметров приводят к выпадению кадров, артефактам, проблемам кодировки и другим повреждениям картинки, которые не хотелось бы видеть на записях с камер видеонаблюдения.
Проверяем энкодинг на голом CPU

Запуск одной копии ffmpeg дает нам такую начальную картину:

Загрузка по ядрам процессора в среднем на уровне 18–20%, а вывод ffmpeg показывает следующее:
frame=196 fps=87 q=-1.0 Lsize=2685kB time=00:00:06.43 bitrate=3419.0kbits/s speed=2.84x
Запас есть, и можно попробовать сразу три потока:
frame=310 fps=54 q=29.0 size=4608kB time=00:00:07.63 bitrate=4945.3kbits/s speed=1.33x
Четыре потока выбирают почти все мощности CPU и «отъедают» 13 ГБ оперативной памяти.

При этом вывод ffmpeg показывает, что резервы не исчерпаны:
frame=332 fps=49 q=29.0 size=3072kB time=00:00:08.36 bitrate=3007.9kbits/s speed=1.23x
Увеличиваем количество потоков до пяти. Процессор держится на пределе, местами начинаются просадки скорости кадров и битрейта на 5–10%:
frame=491 fps=37 q=29.0 size=4864kB time=00:00:13.66 bitrate=2915.6kbits/s speed=1.03x
Запуск шести потоков показывает, что предел достигнут. Мы все больше и больше отстаем от реального времени и начинаем пропускать кадры:
frame=140 fps=23 q=29.0 size=1024kB time=00:00:01.96 bitrate=2954.4kbits/s speed=0.446x
Включаем мощности GPU
Запускаем один поток ffmpeg с энкодингом через h264_nvenc. Убеждаемся через вывод nvidia-smi, что у нас задействована именно видеокарта:

Поскольку вывод достаточно громоздкий, мы будем отслеживать параметры GPU с помощью следующей команды:
nvidia-smi dmon -s pucm
Расшифруем обозначения:
- • pwr — потребляемая видеокартой мощность в ваттах;
- • gtemp — температура видеоядра (в градусах Цельсия);
- • sm — SM, mem — память, enc — энкодер, dec — декодер (утилизация их ресурсов указана в процентах);
- • mclk — текущая частота памяти (в МГц), pclk — текущая частота процессора (в МГц);
- • fb — использование кадрового буфера (в Мб).
gpu | pwr | gtemp | mtemp | sm | mem | enc | dec | mclk | pclk | fb | bar1 |
---|---|---|---|---|---|---|---|---|---|---|---|
Idx | W | C | C | % | % | % | % | MHz | MHz | MB | MB |
0 | 35 | 48 | - | 1 | 0 | 6 | 0 | 6500 | 1560 | 213 | 5 |
gpu | Idx | 0 |
pwr | W | 35 |
gtemp | C | 48 |
mtemp | C | - |
sm | % | 1 |
mem | % | 0 |
enc | % | 6 |
dec | % | 0 |
mclk | MHz | 6500 |
pclk | MHz | 1560 |
fb | MB | 213 |
bar1 | MB | 5 |
Нас в этом выводе будут интересовать значения загрузки энкодеров GPU и утилизации видеопамяти.
Вывод ffmpeg дает следующие результаты:
frame=192 fps=96 q=23.0 Lsize=1575kB time=00:00:06.36 bitrate=2027.1kbits/s speed=3.17x
Запускаем сразу пять потоков. Как видно из вывода htop, в случае энкодинга на GPU загрузка процессора минимальна, а большая часть работы ложится именно на видеокарту. Дисковая подсистема также загружена гораздо меньше.

gpu | pwr | gtemp | mtemp | sm | mem | enc | dec | mclk | pclk | fb | bar1 |
---|---|---|---|---|---|---|---|---|---|---|---|
Idx | W | C | C | % | % | % | % | MHz | MHz | MB | MB |
0 | 36 | 48 | - | 8 | 2 | 40 | 0 | 6500 | 1560 | 1035 | 14 |
gpu | Idx | 0 |
pwr | W | 36 |
gtemp | C | 48 |
mtemp | C | - |
sm | % | 8 |
mem | % | 2 |
enc | % | 40 |
dec | % | 0 |
mclk | MHz | 6500 |
pclk | MHz | 1560 |
fb | MB | 1035 |
bar1 | MB | 14 |
Загрузка блоков энкодинга увеличилась до 40%, память мы заняли почти на гигабайт, но видеокарта по факту загружена не сильно. Вывод ffmpeg подтверждает это, показывая, что у нас есть ресурсы для увеличения количества потоков минимум в 2 раза:
frame=239 fps=67 q=36.0 Lsize=2063kB time=00:00:07.93 bitrate=2130.3kbits/s speed=2.22x
Ставим десять потоков. Утилизация CPU на уровне 15–20%.

Параметры видеокарты:
gpu | pwr | gtemp | mtemp | sm | mem | enc | dec | mclk | pclk | fb | bar1 |
---|---|---|---|---|---|---|---|---|---|---|---|
Idx | W | C | C | % | % | % | % | MHz | MHz | MB | MB |
0 | 55 | 48 | - | 14 | 4 | 61 | 0 | 6500 | 1920 | 2064 | 24 |
gpu | Idx | 0 |
pwr | W | 55 |
gtemp | C | 48 |
mtemp | C | - |
sm | % | 14 |
mem | % | 4 |
enc | % | 61 |
dec | % | 0 |
mclk | MHz | 6500 |
pclk | MHz | 1920 |
fb | MB | 2064 |
bar1 | MB | 24 |
Потребление электроэнергии возросло, видеокарта вынуждена была разогнать частоту видеоядра, но мощности энкодинга и видеопамять позволяют увеличивать нагрузку. Проверяем вывод ffmpeg, чтобы в этом убедиться:
frame=1401 fps=36 q=29.0 Lsize=12085kB time=00:00:46.66 bitrate=2121.5kbits/s speed=1.2x
Пробуем добавить еще четыре потока и получаем загрузку блоков энкодинга в 100%.
gpu | pwr | gtemp | mtemp | sm | mem | enc | dec | mclk | pclk | fb | bar1 |
---|---|---|---|---|---|---|---|---|---|---|---|
Idx | W | C | C | % | % | % | % | MHz | MHz | MB | MB |
0 | 68 | 59 | - | 18 | 7 | 100 | 0 | 6500 | 1920 | 2886 | 33 |
gpu | Idx | 0 |
pwr | W | 68 |
gtemp | C | 59 |
mtemp | C | - |
sm | % | 18 |
mem | % | 7 |
enc | % | 100 |
dec | % | 0 |
mclk | MHz | 6500 |
pclk | MHz | 1920 |
fb | MB | 2886 |
bar1 | MB | 33 |
Вывод ffmpeg подтверждает, что мы достигли предела. Утилизация CPU при этом все еще не превышает 20%.
frame=668 fps=31 q=26.0 Lsize=5968kB time=00:00:22.23 bitrate=2199.0kbits/s speed=1.04x
Контрольные 15 потоков показывают, что GPU начинает сдавать, так как блоки энкодинга работают с перегрузкой, а также наблюдается рост температуры и потребляемой мощности.
gpu | pwr | gtemp | mtemp | sm | mem | enc | dec | mclk | pclk | fb | bar1 |
---|---|---|---|---|---|---|---|---|---|---|---|
Idx | W | C | C | % | % | % | % | MHz | MHz | MB | MB |
0 | 70 | 63 | - | 18 | 7 | 100 | 0 | 6500 | 1920 | 3092 | 35 |
gpu | Idx | 0 |
pwr | W | 70 |
gtemp | C | 63 |
mtemp | C | - |
sm | % | 18 |
mem | % | 7 |
enc | % | 100 |
dec | % | 0 |
mclk | MHz | 6500 |
pclk | MHz | 1920 |
fb | MB | 3092 |
bar1 | MB | 35 |
Ffmpeg также подтверждает, что видеокарте становится тяжеловато. Частота обработки и пропуск кадров уже не внушают оптимизма:
frame=310 fps=28 q=29.0 size=2560kB time=00:00:10.23 bitrate=2049.4kbits/s speed=0.939x
CPU vs GPU
Подытожим: применение GPU в такой конфигурации можно назвать оправданным, поскольку максимальное количество обрабатываемых видеокартой потоков в 3 раза превышает возможности далеко не самых слабых процессоров (особенно без поддержки технологий аппаратного кодирования). С другой стороны, мы используем только минимальную часть возможностей видеоадаптера. Поскольку остальные его блоки и видеопамять не сильно нагружены, ресурсы дорогостоящего устройства утилизируются неэффективно.
Искушенные читатели могут заметить, что мы не проверили работу в режимах 2K/4K, не использовали возможности современных кодеков (наподобие h265 и VP8/9), а также установили в тестовый стенд видеоадаптер на архитектуре предыдущего поколения. Тот же A5000 должен показать лучший результат, но его работу мы проверим в следующей статье, а потом будем препарировать Intel Quick Sync.
Напишите в комментариях, какие еще нюансы стоит учесть при тестировании, какие моменты мы упустили и что бы вы хотели узнать по этой теме.