Пытался тут в очередной раз ускорить расчет векторного произведения для звуковых данных. С подачи Ивана в комментариях к моему посту о том, что нужно выполнять сразу четыре произведения, что должно дать существенный эффект.
Однако, для того, что бы оптимизировать, надо найти основу, с чего начинать. В качестве основы я взял, да и развернул циклы по векторному произведению для того простейшего случая, когда длина вектора равна 4. С включенным оптимизатором.
Естественно, выглядит такой код странно. Переписал его же на цикл. Скорость выполнения снизилась почти в два раза. Чему, честно сказать, я удивился. Ни конвейер, ни суперскалярная архитектура и прочие достижения современного процессоростроения ничего не смогли противопоставить старой, как мир, оптимизации в лоб.
После чего я перешел к оптимизации под SSE. Основная проблема тут состоит в том, что изначально данные расположены не так, как было бы удобно их вычислять с использованием SSE. Но так как мне было любопытно, какой выигрыш даст SSE в том случае, если данные уже расположены правильно, то я сделал вид, что они лежат типа правильно.
Результат оказался забавным. Умножение сразу четырех векторов было почти в три раза быстрее, чем скалярный расчет с использованием цикла, и всего лишь в полтора раза быстрее скалярного варианта с развертыванием циклов.
Вот тут уж современные достижения в скалярных вычислениях продемонстрировали себя полностью. Также складывается впечатление, что для скалярных и векторных операции используется одни и те же блоки арифметических операций с плавающей точкой.
Но засада состоит все же в том, что в реальности данные организованы не так, как нужно для корректных вычислений. Поэтому на пути к векторизации вычислений лежит преграда в виде транспонирования векторов. Это потребует дополнительных операций и пока не понятно, даст ли в результате SSE какой-то выигрыш и если даст, то какой?
Набор команд SSE меня слегка разочаровал. Нет, конечно, очень важно сделать векторные команды. Но не менее важно иметь и команды, которые готовят данные к векторным операциям. В данном случае меня интересовали команды сбора данных из разных участков памяти в один вектор, а также команда умножения вектора на скаляр.
Первая из требуемых мне команд появилась лишь в наборе AVX2. Что касается второй команды, то ее нет вовсе. Что, в принципе понятно, так как умножение на скаляр можно заменить скалярным умножением на вектор, заполненный одним и тем же числом. Однако, такая команда, которая умеет весь вектор заполнять одним числом, также появляется лишь в AVX2.
Беда же состоит в том, что мой процессор, хоть и не самый старый, тем не менее AVX2 не поддерживает. Поэтому вкусить немного радости от его использования не удалось. Пришлось ограничится скалярным вариантом. В этом и состоит разочарование и вопрос: неужели в большинстве прикладных задач, требующих векторных вычислений, данные уже лежать так, как надо?
В результате выигрыш от векторизации есть. И для случая векторного произведения он составляет целых 21.5%!
Вот более детальные результаты:
| Тест | Результат, такты |
| Развернутый скалярный цикл | 642 |
| Скалярный цикл | 1149 |
| SSE с формирование вектора из скаляра | 436 |
| SSE с использованием готового вектора, сформ. из скаляра | 425 |
| SSE+Транспонирование с формирование вектора из скаляра | 504 |
| SSE+Транспонирование с использованием готового вектора, сформ. из скаляра | 630 |
Хотя, возможно, я просто не умею их готовить (вектора в смысле)?
На всякий случай выложу фрагмент кода, с помощью которого получил указанные выше результаты. Код писан для случая wavelet-преобразования, поэтому на самом деле рассчитывается V1*V2 и V1*V3, то есть реально рассчитывается 8 векторных произведений, каждый вектор длиной 4.