vpermpd
должен замедлять вас только в том случае, если узкое место имеет пропускную способность внешнего интерфейса (подача мопов в ядро не в порядке).
vpermpd
не особенно "медленный"если вы не используете процессор AMD.(Перемешивание YMM при пересечении полосы пропускания происходит медленно на процессорах AMD, потому что они должны декодировать больше, чем обычные 2 128-битных мопа, на которые разбиты 256-битные инструкции. vpermpd
- это 3 мопа для Райзена, или 4 систочник памяти.)
В Intel vpermpd
с источником памяти всегда составляет 2 моп для интерфейса (даже неиндексированный режим адресации не может обеспечить микроплавкость).Bu
Если ваш цикл выполняется только для небольшого числа итераций, то OoO exec может скрыть задержку FMA и, возможно, на самом деле узкое место во внешнем интерфейсе для этого цикла + окружающий код ,Это возможно, учитывая, сколько подсчитывает (неэффективный) код горизонтальной суммы за пределами цикла.
В этом случае, возможно, развертывание на 2 поможет, но, возможно, дополнительные издержки, чтобы проверить, можете ли вы запустить дажеодна итерация основного цикла может обойтись очень дорого для очень маленьких подсчетов.
В противном случае (для больших подсчетов) ваше узкое место, вероятно, зависит от переноса цикла от 4 до 5 циклов выполнения FMA.с d2v
в качестве операнда ввода / вывода .Развертывание с несколькими аккумуляторами и увеличение указателя вместо индексации было бы огромным выигрышем в производительности.Как 2x или 3x.
Попробуйте clang, он обычно сделает это за вас, и его настройки skylake / haswell развернуты довольно агрессивно.(например, clang -O3 -march=native -ffast-math
)
GCC с -funroll-loops
фактически не использует несколько аккумуляторов, IIRC.Некоторое время я не смотрел, возможно, я ошибаюсь, но я думаю, что он просто повторяет тело цикла, используя тот же регистр аккумулятора, совсем не помогая запускать больше цепочек dep параллельно.На самом деле Clang будет использовать 2 или 4 разных векторных регистра для хранения частичных сумм для d2v
и добавления их в конце вне цикла.(Но для больших размеров было бы лучше 8 или более. Почему Мулсс занимает всего 3 цикла на Haswell, в отличие от таблиц инструкций Агнера? )
Развертывание будеттакже целесообразно использовать приращение указателя, сохраняя 1 мегапиксель в каждой из команд vaddpd
и vfmadd
для семейства Intel SnB.
Почему m_f.size();
сохраняется впамять (cmp rax, [rsp+0x50]
) вместо регистра? Вы компилируете с отключенным строгим псевдонимом?Цикл не записывает память, так что это просто странно.Если компилятор не считает, что цикл будет выполняться очень мало итераций, поэтому не стоит кода вне цикла для загрузки max?
Копирование и отрицание j
каждая итерация выглядит как упущенная оптимизация.Очевидно, более эффективно начинать с 2-х регистров вне цикла и add rax,0x20
/ sub rbx, 0x20
каждой итерации цикла вместо MOV + NEG.
Если у вас есть [mcve], это выглядит как несколько пропущенныхоптимизации, о которых можно сообщать как об ошибках компилятора.Этот ассм выглядит как вывод gcc для меня.
Обидно, что gcc использует такую ужасную идиому горизонтальной суммы.VHADDPD - 3 мопа, 2 из которых нуждаются в порту случайного воспроизведенияВозможно, попробуйте более новую версию GCC, например, 8.2.Хотя я не уверен, что устранение VHADDPS / PD было частью закрытия ошибки GCC 80846 как исправлено.Эта ссылка на мой комментарий об ошибке при анализе кода hsum в GCC с использованием упакованного сингла с использованием vhaddps
дважды.
Похоже, что ваш hsum после цикла действительно "горячий", поэтому вы страдаете отКомпактный, но неэффективный гсч.