В 2012 году на Хабре уже была моя статья про быстрое сжатие в JPEG на видеокарте. С тех пор прошло уже довольно много времени и мне хотелось бы в общих чертах рассказать про результаты, которые были получены по этой теме. Надеюсь, многим будет интересно узнать, какой уровень производительности можно получить на современных видеокартах NVIDIA при решении практических задач на CUDA.

При обсуждении времени работы алгоритма сжатия, я буду приводить результаты для измерений времени работы на видеокарте без учёта загрузки и выгрузки данных. Этот подход имеет смысл при организации сложных вычислительных систем, когда все расчёты делаются на видеокарте и для большинства стадий общей схемы обработки начальные и конечные данные располагаются в памяти видеокарты. Также существуют методы копирования данных в видеокарту и обратно одновременно с вычислениями, поэтому обсуждение производительности в терминах времени работы алгоритма именно на видеокарте вполне оправдано. Все измерения сделаны с помощью NVIDIA Visual Profiler. Алгоритм JPEG реализован в базовом варианте (Baseline JPEG), т. е. 8 бит на канал, со стандартными таблицами квантования и Хаффмана, без арифметического кодирования и без прогрессивного режима.

Скорость кодирования в JPEG на CUDA со времени прошлой публикации выросла значительно и теперь за 0,78 мс на видеокарте NVIDIA GeForce GTX 1080 можно сжать в 10 раз (качество 90%) 24-битную картинку формата 4К (3840 x 2160) с дискретизацией 4:2:0, что соответствует производительности кодирования порядка 30 Гбайт/с. Для изображений формата 8К и более, скорость сжатия при аналогичных параметрах может быть ещё почти в полтора раза выше.

С одной стороны, это заслуга NVIDIA, которая выпускает все более быстрые видеокарты. С другой, это наш результат распараллеливания и оптимизации алгоритма JPEG на CUDA. Время пересылки несжатого изображения формата 4К из оперативной памяти через PCI-Express x16 (Gen3) в эту видеокарту равно 2,17 мс. Получаем интересный результат: сжатие в JPEG теперь можно сделать в два с половиной раза быстрее по сравнению со скоростью копирования несжатых данных через PCI-Express x16. Скорость декодирования у нас пока сильно отстаёт от скорости кодирования, а аналогичную 4К картинку мы можем декодировать за 2,6 мс. В ближайшее время надеемся устранить этот разрыв между кодером и декодером.

cuda jpeg benchmark

Такая высокая производительность нужна в тех случаях, когда требуется обрабатывать большие массивы или потоки данных. Для сохранения в JPEG одиночных изображений в графическом редакторе такая скорость не нужна, хотя режим пачки вполне востребован. Быстрым кодированием и декодированием в основном интересуются разработчики приложений для визуализации 3D и VR, потому что удобно хранить исходные данные в виде джипегов, копировать их в видеокарту, а уже там делать быстрое декодирование и вывод на монитор или на очки через OpenGL. Таким образом, можно добиться высокой частоты кадров при большом разрешении, например, 100-120 к/с для 12-мегапиксельных изображений. Важным условием быстрого декодирования является наличие внутри джипегов необходимого количества рестарт-маркеров, которые позволяют достигать высокой степени распараллеливания при декодировании. Без этих рестарт-маркеров скорость декодирования JPEG падает на порядок или даже больше. Наш кодер ставит эти маркеры по умолчанию, но их также можно добавлять в оффлайне с помощью утилит типа jpegtran, после чего станет возможным быстрое декодирование джипегов на видеокарте.

Для работы с современными видеокамерами мы реализовали на CUDA и 12-битный кодер JPEG, который тоже имеет очень высокую производительность. Он сжимает 12-битную картинку 4К с качеством 90% и дискретизацией 4:2:0 на видеокарте GeForce GTX 1080 за 1,2 мс. Для большинства видеокамер (мы работаем в основном с промышленными и скоростными камерами) диапазон в 12 бит покрывает основные потребности, а высокая производительность сжатия позволяет решать массу задач в реальном времени даже при очень высокой частоте кадров.

Кроме кодека JPEG мы также реализовали на CUDA алгоритмы демозаики, ресайза, подавления шума и др. Фактически, это набор быстрых параллельных алгоритмов для обработки RAW данных на видеокарте. Эти решения обеспечивают практически полный цикл предварительной обработки изображений от видеокамер и они работают на всех видеокартах NVIDIA, в том числе и на мобильных Tegra K1 и Tegra X1. В ближайшее время на базе этого функционала мы выпустим приложение на CUDA для обработки в реальном времени серий изображений формата DNG от кинокамер BlackMagic Design.

Более подробные бенчмарки кодека JPEG, демозаика, ресайза, шумодава и кодера JPEG2000 можно посмотреть тут.
Поделиться с друзьями
-->

Комментарии (27)


  1. alexkuzko
    15.06.2016 19:30

    А что насчет предыдущего поколения? GTX980?


    1. fyodorser
      15.06.2016 21:17
      +1

      На GeForce GTX 980 кодирование в JPEG с теми же параметрами для картинки 4К даёт 1,3 мс. Всем желающим по запросу могу в личку прислать документ с бенчмарками.


      1. ComradeAndrew
        15.06.2016 22:11
        +1

        А можете ли вы его выложить в конце статьи? Я думаю многим будет интересно посмотреть.


        1. fyodorser
          15.06.2016 22:21

          Версия на английском тут. Вариант на русском сделаю и выложу. Скорее всего, завтра.


          1. fyodorser
            16.06.2016 15:08

            Ссылка на документ с бенчмарками теперь есть в конце статьи.


  1. erlyvideo
    15.06.2016 20:50

    у вас опенсорс или покупать?
    если опенсорс, то где скачать, если покупать, то расскажите про условия покупки и ценник


    1. fyodorser
      15.06.2016 21:21

      Это не опенсорс. Демо SDK и документацию могу выслать по запросу. Цены и условия обсуждать здесь не стоит.


      1. erlyvideo
        15.06.2016 22:23

        да, конечно.

        Если не сложно, то на max@erlyvideo.org или могу я вам написать.


        1. fyodorser
          15.06.2016 22:59

          Завтра пришлю.


  1. leshabirukov
    15.06.2016 21:49

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


  1. fyodorser
    15.06.2016 21:56
    +1

    С одной стороны — это самостоятельный продукт, с другой — часть нашего SDK для обработки изображений на видеокартах NVIDIA. Скоростные видеокамеры — это лишь один из многих вариантов возможных приложений. Сейчас большинство наших заказчиков занимаются 3D и VR, но есть и масса других приложений, в том числе и с камерами.


  1. gxcreator
    16.06.2016 01:27

    Интересно было бы посмотреть результаты на OpenCL + APU, по идее там копировать данные не нужно.


    1. fyodorser
      16.06.2016 12:19

      Мне было бы тоже интересно на это посмотреть, но я не встречал бенчмарков для кодека JPEG на OpenCL. Если найдёте, пожалуйста расскажите или дайте ссылку.


      1. Salabar
        16.06.2016 18:14

        Радеоновский драйвер подменяет родную либу Винды на свой декодер, если кому-то интересно это проверить. Почему JPEG, кстати? Если для VR, то логичнее сразу кодировать в fixed bitrate формат, который из коробки читается видеокартой. Даже если сжатие меньше, производительность непосредственно VR возрастет. А скачивание миллионов картинок обратно на хоста это какой-то странный юзкейс, в этом случае производительность обработки ограничена половиной скорости PCI-E.


        1. fyodorser
          16.06.2016 20:35

          Радеоновский драйвер подменяет родную либу Винды на свой декодер, если кому-то интересно это проверить.
          Давайте говорить точно: какую либу подменяют, на какой именно декодер, какая у этого декодера производительность, какие у такого кодера порядок сжатия и качество. Без этого любое сравнение вряд ли будет корректным. Тут всё очень конкретно: алгоритм, визуальное качество, степень сжатия, скорость работы. Интересно, что и как делает радеоновский драйвер.

          Почему JPEG, кстати? Если для VR, то логичнее сразу кодировать в fixed bitrate формат, который из коробки читается видеокартой. Даже если сжатие меньше, производительность непосредственно VR возрастет.
          Наши заказчики выбирают JPEG, потому что его можно очень быстро декодировать, визуальное качество для них приемлемое при коэффициенте сжатия порядка 10. Среди требований заказчиков никогда не встречал необходимость fixed bitrate формата. Некоторые алгоритмы жмут лучше, но у них со скоростью проблемы. JPEG – это компромисс, как это всегда и бывает при сжатии с потерями.

          Математика тут простая. Обычно заказчики заранее создают все необходимые изображения. Далее задача состоит в том, чтобы 12-Мпикс картинку показывать на мониторе или в очках с частотой 100 Гц или более. Это значит, что в секунду нам нужно закачать в память видеокарты 12 Мпикс*3*100 = 3600 МБайт. Если скорость шины PCI-Express x16 равна 12 ГБ/с, то один кадр можно передать за 3 мс и это не проблема. Проблема состоит в том, как с SSD передать 3600 МБайт данных в секунду в видеокарту. Мы все изображения заранее сжимаем в 10 раз, после чего с передачей 360 МБ/с нет никаких проблем. Переслать эти данные можно за 0,3 мс, декодировать за 4 мс. В сумме получается больше, чем 3 мс, но меньше 10 мс (100 Гц).

          А скачивание миллионов картинок обратно на хоста это какой-то странный юзкейс, в этом случае производительность обработки ограничена половиной скорости PCI-E.
          Для 3D и VR не нужно качать миллионы картинок на хост, движение идёт в обратном направлении. Нужно с хоста качать картинки в видеокарту.


          1. Salabar
            16.06.2016 22:54

            Я полагаю, люди перебрали кучу вариантов и понимают, что делают, но решение реально неочевидное. Кодируешь всё каким-нибудь ASTC и получаешь примерно те же числа (передача чуть дольше, распаковка чуть быстрее), но переносимо.


            1. fyodorser
              16.06.2016 23:03

              Вы полагаете или знаете? Если знаете, пожалуйста покажите расчёты, хотя бы приблизительные. В чём именно состоит решение? Почему получаются те же числа?


              1. Salabar
                16.06.2016 23:37

                https://upload.wikimedia.org/wikipedia/en/c/c4/Original_Image_before_ASTC_compression.jpg
                https://en.wikipedia.org/wiki/File:Detail_of_ASTC_compressed_image.png
                При битрейте 4 (шестикратное сжатие) бита на пиксель лично я не нашел каких-то искажений мелких деталей (увеличил оригинал в пеинте до 300%, оригинал изначально jpeg, так что черт его знает, насколько на самом деле есть разница). Отсюда считывание с диска будет занимать где-то в 1.8 раз дольше, чем в случае с JPEG. Я не знаю, насколько быстрее будет распаковка, но этот формат был специально разработан для поддержки в железе, в видеокартах есть специальные устройства с отдельным кешом как раз для работы с текстурами. В 10 мс всё должно влезать вообще без проблем.


                1. fyodorser
                  17.06.2016 00:05

                  Как следует из этой ссылки, формат ASTC был разработан и принят в качестве официального расширения для OpenGL и OpenGL ES в 2012 году, т. е. совсем недавно. Карточки AMD его поддерживают, а у NVIDIA такая поддержка есть только в Kepler и в мобильных чипах на Maxwell. Не очень это похоже на общепринятый стандарт.
                  JPEG и Motion JPEG – это давно признанные и широко распространённые стандарты. Поэтому можно просто сделать AVI, который будет работать в любом плеере и который легко разобрать на кадры, оправить их в видеокарту и там декодировать. Для таких решений важна также универсальность, которая есть в случае с JPEG.


                  1. Salabar
                    17.06.2016 00:22

                    Не очень это похоже на общепринятый стандарт.

                    … Поэтому мы берем дорогущую квадру на, видимо, раскаленном Ферми там, где справится затычка с гигом памяти. Ладно, шучу. Я сразу сказал, что раз делают так, а не иначе, значит были причины.


                  1. erlyvideo
                    17.06.2016 08:17

                    я не очень понял, как вы от кодека (jpeg) перешли к паршивому и устаревшему контейнеру avi, который далеко не везде работает (это вообще атавизм из мира виндузятников). Какой кодек вы собираетесь класть в avi?


                    1. fyodorser
                      17.06.2016 10:27

                      Кодек джипег выполняет работу по быстрому кодированию кадров на видеокарте. А укладка сжатых изображений в контейнер не является задачей для CUDA, с этим хорошо справляется FFmpeg. Так получилось, что в большинстве задач для видеокамер наши заказчики просили AVI, поэтому мы так и сделали. Обычно заказчик сам подключает FFmpeg и получает нужный ему контейнер.


  1. rPman
    16.06.2016 23:00

    Это очень круто, но почему выбрали именно CUDA? Вместо OpenCL! Вы же 'намертво' привязали свою разработку к одному вендору железа, и не самому лучшему по соотношению цена-скорость.
    Или все на столько плохо и оптимизация требуется на самом низком уровне, и красивые переносимые решения нужных скоростей не дают?


    1. Salabar
      16.06.2016 23:10
      +1

      OpenCL это тоже на данный момент привязка намертво, потому что NVIDIA (и Apple на Маках) поддержку методично убивает, Knights Landing от Интела для обработки изображений менее эффективен, а для FPGA нужно писать совсем другой код. Полноценно работать можно только с GCN, и только на Линуксе с Виндой. Теперь вы знаете, почему эпоха GPGPU для конечных пользователей не настанет никогда.


    1. fyodorser
      16.06.2016 23:23

      Вы совершенно правы — как только требуется оптимизация на низком уровне, то в любом случае получается непереносимое решение. И уже не важно, это CUDA или OpenCL.

      С нашей точки зрения это лучший вендор, потому что кроме цены и скорости (тут идеала нет ни у кого) есть ещё среда разработки, с которой у NVIDIA всё очень хорошо. А ещё есть мобильные решения, доля рынка видеокарт и пр. Да просто найти программиста по OpenCL — это тоже большая проблема. Мы видим больше плюсов у NVIDIA.


  1. dimaleks
    17.06.2016 08:06

    Спасибо за статью, приятно видеть, что Cuda используется в коммерческих проектах. Сам сейчас работаю в академической среде, во многом с кудой.
    У меня вопрос: у вас есть ссылки на доступное описание алгоритма сжатия JPEG? Интересно оценить сложность задачи. Судя по вашей производительности в 30 ГБ/c это compute-bound метод?
    И еще один: вы не интересовались новой связкой P100 + IBM Power + NVlink? Чтобы устранить задержки от PCI-E?


  1. fyodorser
    17.06.2016 11:00

    Описание алгоритма JPEG можно найти в Википедии или в моей предыдущей статье (ссылка есть в начале текста). Раньше этот алгоритм считался вычислительно сложным, теперь уже можно полагать, что это не совсем корректно. Мне кажется, что даже при такой производительности это memory-bound случай. Большинство улучшений по сравнению с предыдущими версиями были получены благодаря более эффективному использованию разделяемой памяти и регистров, сам алгоритм не изменился. Также очевидна проблема с достижением более полной загрузки видеокарты. Видно, что при увеличении размера данных производительность растёт, и это логично. Для получения большей скорости нужно ещё больше данных, хотя количество вычислений для заданного размера картинки остаётся прежним. Если сразу кодировать несколько изображений (это называется режим пачки), то ухудшится латентность (увеличится время отклика), а производительность станет ещё выше. Мы этот режим ещё не сделали, пока скорости и без него хватает, а в задачах реального времени обычно нужна минимальная латентность. Но режим пачки есть в наших планах, мы его обязательно сделаем.

    Устранять задержки от PCI-E можно разными способами. Скорость у PCI-E довольно высокая, поэтому желательно сразу оценить, какой выигрыш возможен и стоит ли игра свеч. Для максимально быстрого получения данных от видеокамер можно сделать GPUDirect, т. е. модифицировать драйвер так, чтобы он по DMA отправлял данные сразу в видеокарту. Мы так сделали, но результат разочаровал. Сэкономили миллисекунду, а это оказалось совсем мало, поэтому больше так не делаем.
    Ещё можно организовать стримы (CUDA Streams), которые обеспечат загрузку новых изображений во время расчётов с текущим кадром. Так очень часто и делают, но это приводит к дополнительному расходу памяти. Если совместить стримы с пачкой, то получится ещё быстрее.

    Про связку P100 + IBM Power + NVlink ничего сказать не могу.