Pull to refresh

Pillow-SIMD

Reading time 5 min
Views 14K

Ускорение операций в 2.5 раза по сравнению с Pillow и в 10 по сравнению с ImageMagick



Pillow-SIMD — это «форк-последователь» библиотеки работы с изображениями Pillow (которая сама является форком библиотеки PIL, ныне покойной). «Последователь» означает, что проект не становится самостоятельным, а будет обновляться вместе с Pillow и иметь ту же нумерацию версий, только с суффиксом. Я надеюсь более-менее оперативно выпускать версии Pillow-SIMD сразу после выхода версий Pillow.


Почему SIMD


Есть несколько способов улучшения производительности обработки изображений (да и всех остальных вещей, наверное, тоже).


  1. Можно использовать более хорошие алгоритмы, которые дают такой же результат.
  2. Можно сделать более быструю реализацию существующего алгоритма.
  3. Можно подключить больше вычислительных ресурсов для решения той же задачи: дополнительные ядра CPU, GPU.

Самое классное, когда вы можете использовать более быстрый алгоритм, как когда в Pillow 2.7 Гауссово размытие на основе сверток было заменено размытием последовательностью box-фильтров. К сожалению, число таких фокусов весьма ограничено. Также очень заманчива идея использовать больше вычислительных ресурсов. Но к сожалению, часто их либо нет, либо они стоят дополнительных денег (как в случае с арендуемыми серверами). Использовать же GPU для вычислений вообще нетривиальная задача, связанная с подбором определенного железа и правильной настройкой драйверов. Остается самый надежный способ — попытаться заставить существующий код работать быстрее на существующем железе. И тут SIMD-инструкции подходят как нельзя лучше.


SIMD означает: «одна инструкция, много данных» (single instruction, multiple data). В классических программах мы берем операнды, выполняем операцию, сохраняем результат. В случае SIMD мы берем сразу пачку операндов, делаем одно и то же действие над всеми разом и сохраняем пачку результатов. Для процессора это проще, чем несколько раз выполнить одинаковые действия. Существует огромное количество расширений команд процессоров с SIMD-инструкциями, например: MMX, SSE-SSE4, AVX, AVX2, AVX512, NEON.


В текущей версии Pillow-SIMD может быть скомпилирован с использованием расширений SSE4 (по умолчанию), либо AVX2.


Статус проекта


Pillow-SIMD годится для продакшена. Различные версии Pillow-SIMD уже больше года работают на серверах Uploadcare. Uploadcare — это сервис для хранения и обработки пользовательского контента и главный спонсор Pillow-SIMD.


На текущий момент следующий операции ускорены в SIMD-версии:


  • Ресайз (ресемплинг на основе сверток): SSE4, AVX2
  • Гауссово размытие и box-фильтры: SSE4

Производительность


Цифры означают количество обработанных мегапикселей исходного изображения в секунду. Например, если ресайз изображения размером 7712×4352 был выполнен за 0.5 секунд, производительность будет 67.1 Mpx/s.


Уже в процессе редактирования я понял, что у меня кажется путаница и в мегапикселе для ImageMagick 10^6 пикселей, а в мегапикселе для Pillow — 2^20. Но это не сильно влияет на общую картину.


Протестированы:


  • ImageMagick 6.9.3-8 Q8 x86_64
  • Pillow 3.2.0
  • Pillow-SIMD 3.2.0.post2

Source Operation Filter IM Pillow SIMD SSE4 SIMD AVX2
7712×4352 RGB Resize to 16x16 Bilinear 27.0 217 437 710
Bicubic 10.9 115 232 391
Lanczos 6.6 76.1 157 265
Resize to 320x180 Bilinear 32.0 166 410 612
Bicubic 16.5 92.3 211 344
Lanczos 11.0 63.2 136 223
Resize to 2048x1155 Bilinear 20.7 87.6 229 265
Bicubic 12.2 65.7 140 171
Lanczos 8.7 41.3 100 126
Blur 1px 8.1 17.1 37.8
10px 2.6 17.4 39.0
100px 0.3 17.2 39.0
1920×1280 RGB Resize to 16x16 Bilinear 41.6 196 426 750
Bicubic 18.9 102 221 379
Lanczos 13.7 68.6 140 227
Resize to 320x180 Bilinear 27.6 111 303 346
Bicubic 14.5 66.3 164 230
Lanczos 9.8 44.3 108 143
Resize to 2048x1155 Bilinear 9.1 20.7 71.1 69.6
Bicubic 6.3 16.9 53.8 53.1
Lanczos 4.7 14.6 40.7 41.7
Blur 1px 8.7 16.2 35.7
10px 2.8 16.7 35.4
100px 0.4 16.4 36.2

Pillow всегда быстрее, чем ImageMagick, а Pillow-SIMD быстрее чем Pillow примерно в 2-2.5 раза для SSE4-версии. В основном, AVX2-версия оказывается быстрее чем ImageMagick в 10-15 раз.


Тесты выполнялись на Ubuntu 14.04 64-bit, запущенной на процессоре Intel Core i5 4258U с AVX2. Все тесты использовали только одно ядро процессора.


Производительность ImageMagick была измерена утилитой командной строки convert с аргументами -verbose и -bench. Выбранные фильтры в точности соответствуют существующим в Pillow фильтрам:


  • PIL.Image.BILINEAR == Triangle
  • PIL.Image.BICUBIC == Catrom
  • PIL.Image.LANCZOS == Lanczos

Для тестирования были использованы такие скрипты.


Почему Pillow такой быстрый


Тут нет никаких трюков, для тестов использовались высококачественные методы ресайза и размытия. Результаты практически попиксельно совпадают с небольшой погрешностью. Разница только в эффективности самих алгоритмов. В Pillow 2.7 ресемплинг был переписан с использованием предварительно вычисленных коэффициентов, меньшим использованием чисел с плавающей точкой и транспонированием, эффективно использующим кэш процессора.


Почему Pillow-SIMD еще быстрее


Конечно же из-за использования SIMD-команд. Но у меня еще несколько мыслей, как можно улучшить этот результат.


  • Эффективная работа с памятью В настоящий момент каждый пиксель загружается в SSE-регистр из памяти по отдельности, в то время как в один SSE-регистр возможно прочитать 4 пикселя за раз.
  • Вычисления на целых числах Несмотря на то, что современные процессоры очень эффективно работают с числами с плавающей точкой, есть две причины полагать, что работа с целыми числами будет эффективнее: операции над целыми алгоритмически проще; для работы с ними не требуется дополнительных ковертаций.
  • Выравнивание данных в памяти загрузка и выгрузка данных из SIMD-регистров выполняется быстрее, если адреса в памяти, с которыми идет обмен, выровнены.

Почему бы не влить изменения обратно в Pillow


Если коротко — это очень сложно. Pillow поддерживает большое количество архитектур, не только x86. Но даже на x86 Pillow для некоторых платформ распространяется в виде скомпилированных исполняемых файлов. Чтобы иметь возможность использовать SIMD-команды в коде, нужно передавать компилятору аргументы, разрешающие использование самых продвинутых инструкций, которые мы хотим использовать: -mavx2. После этого нужно делать проверку возможностей процессора во время выполнения и включать ту или иную ветку кода в зависимости от них. Проблема в том, что такие аргументы автоматически компируют код, спрятанный под условия препроцессора if (__AVX2__) и ниже, который может ни иметь никаких проверок времени выполнения. Самое печальное, что такой код действительно находится, по крайней мере при компиляции GCC, и исполняемые файлы без явного использования AVX2, но собранные с -mavx2, начинают вылетать. Разумеется, можно собирать разные версии библиотеки с разными опциями компилятора и динамически их подключать, но это [см. начало этого параграфа].


Установка


Хорошие новости, что для установки SSE4 версии достаточно написать как обычно pip install pillow-simd, и если ваш процессор умеет в SSE4 (думаю, вероятность этого около 95%), все пойдет замечательно. Не забудьте удалить оригинальный пакет Pillow.


Если вы хотите собрать AVX2 версию, то нужно передать компилятору дополнительные флаги. Проще всего это сделать, задав переменную окружения CC во время установки и компиляции.


$ pip uninstall -y pillow-simd ; CC="cc -mavx2" pip install pillow-simd

Иногда бывает, что зависимость от Pillow есть не только у вас, но и у других пакетов, которые вы используете. И даже если эти пакеты не особо нуждаются в быстром ресемплинге, они все равно устанавливают Pillow без SIMD, который может импортироваться первым. Для этого может пригодиться такой хак при установке с Гитхаба:


$ pip install -e git+https://github.com/uploadcare/pillow-simd.git@v3.2.0.post3#egg=pillow

Тогда во время установки другого пакета с зависимостью от Pillow, еще одна версия Pillow ставиться не будет:


$ pip install xhtml2pdf -e git+https://github.com/uploadcare/pillow-simd.git@v3.2.0.post3#egg=pillow
Tags:
Hubs:
+23
Comments 5
Comments Comments 5

Articles