Почему нарезанный поток так сильно влияет на кодирование в реальном времени с использованием ffmpeg x264?

Я использую ffmpeg libx264 для кодирования экрана 720p, снятого с x11, в реальном времени с частотой кадров 30. Когда я использую параметр -tune zerolatency, среднее время кодирования на кадр может достигать 12 мс. с базовой линией профиля.

Изучив исходный код ffmpeg x264, я обнаружил, что ключевым параметром, приводящим к такому длительному времени кодирования, является нарезанные потоки, которые включаются с помощью -tune с нулевой задержкой. После отключения с помощью -x264-params sliced-threads=0 время кодирования может составлять всего 2 мс.

А при отключенных нарезанных потоках загрузка ЦП будет составлять 40%, а при включении — только 20%.

Может ли кто-нибудь объяснить подробности об этой нарезанной нитью? Особенно при кодировании в реальном времени (предположим, что кадры не буферизуются для кодирования. Кодируйте только при захвате кадра).


person CurtisGuo    schedule 10.11.2015    source источник
comment
Вы используете preset по умолчанию? Что произойдет, если вы используете -preset ultrafast?   -  person aergistal    schedule 10.11.2015
comment
Сверхбыстрый используется в обоих случаях выше.   -  person CurtisGuo    schedule 10.11.2015
comment
Это интересный вопрос. Используете ли вы последние версии ffmpeg и libx264 и на какой ОС/ЦП. Кроме того, как вы измеряете?   -  person aergistal    schedule 10.11.2015
comment
Это не последняя версия, последняя фиксация моего исходного кода — 23 февраля 2014 г., а libx264 — 11 февраля 2014 г. (извините, что исходный код получен от другого парня, я могу получить подробности только из журнала git) ОС хоста это ubuntu 14.04, а процессор Xeon (R) CPU E5-2630 v3. Я использовал параметр -benchmark_all и сбрасывал все выходные данные в файл, а затем вычислял среднее время кодирования с помощью скрипта.   -  person CurtisGuo    schedule 11.11.2015
comment
x264/doc/threads.txt говорит, что части кодировщика являются последовательными, а многопоточность на основе срезов плохо масштабируется. Поскольку у вас 8 ядер, я думаю, что он порождает 8 потоков срезов. Вы можете переопределить --threads 4 или --slices/--slices-max и посмотреть, что произойдет. Это похоже на вашу проблему: mailman.videolan.org/pipermail /x264-devel/2010-April/ Я не думаю, что дело в планировщике, у вас новое ядро.   -  person aergistal    schedule 11.11.2015
comment
Кажется, что потоки влияют на время кодирования. Как я измерил, использование нарезанного потока и установка потоков = 1 приводит к времени кодирования, скажем, 2,6 мс, в то время как потоки = 16 занимают 4,3 мс. Но при отключении нарезанного потока время кодирования составляет 0,8 мс. Я думаю, что есть еще какой-то алгоритм, влияющий на время кодирования, помимо проблемы с потоками.   -  person CurtisGuo    schedule 12.11.2015
comment
Использование слишком большого количества потоков может привести к снижению производительности, поскольку накладные расходы на их поддержку превышают конечный выигрыш. В документах также отмечается, что многопоточность на основе срезов имеет более низкую пропускную способность. Я думаю, что идея заключается в том, что многопоточность на основе кадров вносит задержку в кадрах. В режиме реального времени с малой задержкой вы хотите отправить кадр как можно скорее, а не суперэффективно кодировать его, поэтому я думаю, что нарезка на основе имеет смысл, поскольку все потоки работают с одним и тем же кадром. Я постараюсь опубликовать ответ, может быть, кто-то добавит на его основе.   -  person aergistal    schedule 12.11.2015


Ответы (1)


показывает, что многопоточность на основе кадров имеет лучшую пропускную способность, чем на основе срезов. Он также отмечает, что последний плохо масштабируется из-за последовательных частей кодировщика.

Ускорение и кодирование потоков для профиля veryfast (не в реальном времени):

threads  speedup       psnr
      slice frame   slice  frame
x264 --preset veryfast --tune psnr --crf 30
 1:   1.00x 1.00x  +0.000 +0.000
 2:   1.41x 2.29x  -0.005 -0.002
 3:   1.70x 3.65x  -0.035 +0.000
 4:   1.96x 3.97x  -0.029 -0.001
 5:   2.10x 3.98x  -0.047 -0.002
 6:   2.29x 3.97x  -0.060 +0.001
 7:   2.36x 3.98x  -0.057 -0.001
 8:   2.43x 3.98x  -0.067 -0.001
 9:         3.96x         +0.000
10:         3.99x         +0.000
11:         4.00x         +0.001
12:         4.00x         +0.001

Основное отличие заключается в том, что многопоточность кадров увеличивает задержку кадра, поскольку для работы требуются разные кадры, в то время как в случае многопоточности на основе слайсов все потоки работают с одним и тем же кадром. При кодировании в реальном времени нужно было бы ждать поступления дополнительных кадров, чтобы заполнить конвейер, в отличие от автономного режима.

Обычная многопоточность, также известная как многопоточность на основе фреймов, использует умную систему ступенчатых фреймов для параллелизма. Но за это приходится платить: как упоминалось ранее, каждый дополнительный поток требует еще одного кадра задержки. Потоки на основе слайсов не имеют такой проблемы: каждый кадр разбивается на слайсы, каждый слайс кодируется на одном ядре, а затем результат объединяется, чтобы сделать окончательный кадр. Его максимальная эффективность намного ниже по целому ряду причин, но он допускает хоть какой-то параллелизм без увеличения задержки.

Из: Дневник разработчика x264

Бессрезовая многопоточность: пример с двумя потоками. Начать кодирование кадра №0. Когда это будет сделано наполовину, начните кодировать кадр №1. Поток № 1 теперь имеет доступ только к верхней половине своего опорного кадра, так как остальная часть еще не закодирована. Поэтому он должен ограничить диапазон поиска движения. Но это, вероятно, нормально (если только вы не используете много потоков на маленьком кадре), поскольку такие длинные векторы вертикального движения встречаются довольно редко. Через некоторое время оба потока закодировали одну строку макроблоков, поэтому поток № 1 по-прежнему может использовать диапазон движения = +/- 1/2 высоты кадра. Еще позже поток №0 заканчивает кадр №0 и переходит к кадру №2. Поток № 0 теперь получает ограничения на движение, а поток № 1 — без ограничений.

Из: http://web.archive.org/web/20150307123140/http://akuvian.org/src/x264/sliceless_threads.txt

Поэтому имеет смысл включить sliced-threads с -tune zereolatency, поскольку вам нужно отправить кадр как можно скорее, а не эффективно его кодировать (с точки зрения производительности и качества).

Наоборот, использование слишком большого количества потоков может повлиять на производительность, поскольку накладные расходы на их поддержку могут превысить потенциальный выигрыш.

person aergistal    schedule 12.11.2015
comment
«При кодировании в реальном времени нужно было бы ждать поступления большего количества кадров, чтобы заполнить конвейер, в отличие от автономного режима». Это говорит о резьбе кадра, верно? И как слайс, так и кадрирование увеличивают время декодирования? А номер нити? Спасибо - person CurtisGuo; 13.11.2015
comment
Да, я говорил о фрейм-трединге, так как он работает с разными фреймами. По умолчанию это потоки кадров (#threads = 1,5 * ядра), и imo, поэтому вы видите более низкие значения при включении слайсов. Слишком много потоков (16) = слишком много накладных расходов. Что касается времени декодирования, кажется, что использование слайсов позволяет декодеру использовать преимущества многопоточности и быстрее декодировать (например, для Blu-ray требуется 4 слайса). - person aergistal; 13.11.2015
comment
Еще одна вещь, которая меня интересует, заключается в том, что если кадр b не используется, почему кодировщик ждет более поздние кадры, а не использует только предыдущие кадры. - person CurtisGuo; 13.11.2015
comment
Смотрите мой обновленный ответ. Каждый дополнительный поток увеличивает задержку на 1 кадр, так как это необходимо для оценки движения. - person aergistal; 13.11.2015
comment
Большое спасибо за терпение и подробный ответ. Это действительно очень помогает мне. - person CurtisGuo; 14.11.2015