Как стать автором
Обновить

Комментарии 17

Любопытно попробовать применить AVX512 для медианной фильтрации с ещё большим окном — 11 и выше. Лет пятнадцать назад у меня была такая задачка — реализовать медианный фильтр с окном до 31. Я сделал это через алгоритм Хуанга. Это «бегущая» медиана с использованием гистограммы. Суть в том, что первое значение вычисляется «классически» сортировкой, но одновременно строится гистограмма. При каждом следующем значении из гистограммы викидывается элемент за окном и добавляется новый, после чего значение медианы корректируется так, чтобы слева и справа осталось одинаковое число элементов (ну в этом суть медианы и есть). Я не упирался в SIMD команды, мне хватило скорости, но чую, потенциал там точно есть. У меня данные были 16 бит, так что в гистограмме было всего 65К элементов, но алгоритм и по числам с плавающей точкой будет работать, если разбить 32 или 64 бита на блоки по 16 бит.

Высказанные мной тут идеи в какой-то мере применимы до окна 19 включительно, но я пока не готов их проверять в коде :) Особенно с учетом "параллельного" алгоритма из соседней ветки комментариев и частичной применимости тех же идей к нему — слишком много вариантов, которые надо воплотить в коде.
Надо заметить, что все это применимо только к 1D-фильтру (зато в 1D первый шаг оптимизации должен быть применим и к алгоритму Хуанга — надо будет только обновлять гистограмму по два элемента подряд, и вычислять помимо медианного индекса еще индекс предыдущего элемента, что делается фактически автоматом).
Если именно про векторизацию и Хуанга — алгоритм не очень хороший для векторизации. Для малых размерностей элементов (<=8 битных), на первый взгляд, можно без особых проблем все сделать просто через N гистограмм — но ускорения в N раз не будет. Для больших размерностей, где размер гистограммы оказывается большим — остается разве что оптимизировать какие-то детали алгоритма. Лучше сначала оптимизировать сам алгоритм, благо что научный народ уже этим занимается и нам остается просто подсмотреть у них :)

Теперь придётся выступить на CppCon 2021
Спасибо за статью.
Не сравнивали AVX-512 с другими SIMD — SSE/AVX2? Вот например:
habr.com/ru/post/204682
То есть можно догадаться, что в идеале AVX-512 должен быть в 2 раза быстрее AVX2, поскольку вектор в 2 раза шире. Но все эти сдвиги и перетасовки данных не добавляют ли нагрузки?

Также интересно, на каком железе вы тестируете. И будет ли оно работать IceLake-U — вроде как там поддерживаются не все подмножества AVX-512.
А, пардон, до меня дошло, что у вас окно 7*1 и напрямую сравнивать с кодом по приведённой ссылке (там 3*3 для картинок) нельзя.
Но вопрос про железо остаётся в силе.

Идея из той статьи вполне применима, и вообще более "правильна" с точки зрения SIMD (делать одну операцию над несколькими данными, а не пытаться ускорять работу единственной операции). Назову ее "параллельный вариант". Портировал на AVX512 (единственное, что в отличие от варианта из статьи, для загрузки линий сети используется примитив от Боба, а не многократное чтение из памяти — так проще было делать и автоматически получается обработка краев), подставил частичную сеть для 7 линий, прогнал через бенчмарк и… оно быстрее в 1.64x раза, чем мой лучший результат. Посыпаю голову пеплом, ухожу в монастырь :)
Может быть, на больших окнах результат будет несколько другой. у Боба сеть работает параллельно, а у тов. ErmIg — последовательно и сложность будет расти гораздо быстрее. С другой стороны, 16 результатов на выходе против двух — это весомый аргумент, с которым тяжело бороться. Как выдастся свободное время, я еще подумаю, нельзя ли скомбинировать оба варианта.
Насечет AVX2: казалось бы AVX-512 должен быть в 2 раза быстрее; даже больше, чем в 2 раза — у него система команд богаче (одни масочные операции чего стоят). Но у AVX-512 есть неприятная особенность, что процессор при их использовании может начать тормозить. Я добавил вариант того же "параллельного" алгоритма ErmIg для AVX2, и он всего в 1.36x раз медленнее.
Про железо: я использую Xeon на архитектуре Skylake. Все подмножества AVX-512 не поддерживает никто — Intel клепает их быстрее, чем успевает делать под них процессоры. В моем коде используется только подмножество F, которое работает на всех процессорах, заявляющих про AVX-512.

Идея из той статьи вполне применима, и вообще более «правильна» с точки зрения SIMD (делать одну операцию над несколькими данными, а не пытаться ускорять работу единственной операции).
Ну да, это удобно ещё и в том смысле, что логически векторный код не отличается от скалярного. В реализации ErmIg основная сортирующая функция вообще почти не меняется, благо интринсики спрятаны на более низком уровне.
Я добавил вариант того же «параллельного» алгоритма ErmIg для AVX2, и он всего в 1.36x раз медленнее.
Понятно. ErmIg получил почти такую же разницу между AVX2 и SSE2 — 1.33.
Похоже это общая закономерность SIMD-оптимизаций, наибольший прирост даёт SSE (хотя тоже не всегда пропорционально размеру вектора), а c AVX+ темп снижается.
В статье сравнивалась медианная фильтрация с окном 3x3. В ней сравнительно мало операций. Изображение сравнительно большое ~2MB (входное и выходное влезает в L3, но не влезает в L2). Потому уже начиная с AVX2 узким местом становится именно загрузка данных, а не сами операции с ними.
Если увеличить размер фильтра (хотя бы до 5x5), то ускорение от AVX2 и AVX-512BW значительно увеличиться.
Собственно в Simd есть реализация медианного фильтра с окном 3х3 и 5х5, с которыми там можно поиграться во встроенных тестах.
Для окна 5x5 AVX2 дает ускорение практически в 100 раз, AVX-512BW почти в 130 раз.

Я тут еще подумал и пришел к выводу, что те же идеи из статьи в какой-то мере можно применить и к 2D-фильтрации, и даже более того — там они будут гораздо натуральнее выглядеть, без необходимости крутить местами элементы в регистрах. Как будет время, попробую, чтобы не быть голословным.


Потому уже начиная с AVX2 узким местом становится именно загрузка данных, а не сами операции с ними.
Это очень круто :) Но иногда есть смысл оптимизировать дальше, даже если уже уперлись в скорость L3 — например, при композиции нескольких локальных операций можно обрабатывать данные по кусочкам, меняя оверхед многократной обработки краев на роскошь работы в L1/L2

Поигрался, получилось вроде неплохо. Я обновил статью применительно как раз к фильтру 5x5 из вашей библиотеки.

Круто! Я честно говоря сам думал о похожей оптимизации, но все руки не доходили. Я добавлю ваш код в основную ветку.
P.S. Возникает вопрос: почему ускорение от AVX-512 не в два раза, а только на 30-35%? Дело в том, что в SkylakeX только 1 AVX-512 модуль, который выполняет операции вычисления целочисленных min/max, в тоже время у него 2 AVX-256 таких модуля, которые могут быть задействованы параллельно.
Если я правильно помню, то у i7-7xxxx один AVX512 unit, у i9-7xxx два, у более новых (9xxx, 10xxx) — у всех по два.
А вот частоту при использовании AVX512 — понижают («не так повышают»). Ну и больше шансов получить память как узкое место.
Не все AVX512 юниты одинаково полезны. Во втором юните дублируются только наиболее востребованные операции. В документации в описании конкретной операции есть параметр Throughput (CPI), который по сути обратная величина от максимального количества таких операций за такт.
О, если рассматривать CPI как аналог «единица делить на число юнитов» (почему бы и нет), то про Icelake получается интересно, сумматоров там два, а fma только один.

И про более старые процессоры интересно, получается второй (SSE) юнит у многих кастрирован.
То есть можно догадаться, что в идеале AVX-512 должен быть в 2 раза быстрее AVX2, поскольку вектор в 2 раза шире. Но все эти сдвиги и перетасовки данных не добавляют ли нагрузки?
в идеале вы хотите упереться в быстродействие памяти, которое не зависит от ширины регистра. Если такое быстродействие недостижимо, вы всё равно не получите двойного прироста за счет одного лишь удвоения ширины регистра, т.к. молотя AVX512 процессор начинает сбавлять частоты. Плюс, как ответили некоторые другие комментаторы, количество некоторых вычислительных блоков процессора ограничено, и может возникнуть ситуация, что вы одинаково упретесь в него. Вот и получается что оптимистический прогноз — порядка 30% прироста от AVX512 относительно AVX2. Единственное что может дать более существенное ускорение — использование эзотерических подмножеств команд, но только в тех случаях, где они хорошо накладываются на алгоритм
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории