векторизованная сумма в Fortran
я компилирую мой Fortran
код, используя gfortran
и -mavx
и проверили, что некоторые инструкции векторизованы через objdump
, но я не получаю улучшения скорости, которые я ожидал, поэтому я хочу убедиться, что следующий аргумент векторизуется (эта единственная инструкция составляет ~50% от времени выполнения).
Я знаю, что некоторые инструкции могут быть векторизованы, а другие не могут, поэтому я хочу убедиться, что это может быть:
sum(A(i1:i2,ir))
опять же, эта одна строка занимает около 50% времени выполнения, так как я делаю это над очень большой матрицей. Я могу дать больше информации о почему Я делаю это, но достаточно сказать, что это необходимо, хотя при необходимости я могу реструктурировать память (например, я мог бы сделать сумму как sum(A(ir,i1:i2))
если бы это можно было векторизовать вместо этого.
эта линия векторизуется? Как я могу сказать? Как заставить векторизацию, если это не векторизация?
EDIT: благодаря комментариям я теперь понимаю, что могу проверить векторизацию этого суммирования через -ftree-vectorizer-verbose
и видим, что это не векторизации. Я реструктурировал код следующим образом:
tsum = 0.0d0
tn = i2 - i1 + 1
tvec(1:tn) = A(i1:i2, ir)
do ii = 1,tn
tsum = tsum + tvec(ii)
enddo
и только vectorizes когда я включаю -funsafe-math-optimizations
, но я вижу еще 70% увеличение скорости из-за векторизации. Вопрос остается открытым: Почему?--5--> не векторизировать и как я могу получить просто sum
векторизовать?
2 ответов
оказывается, что я не могу использовать векторизацию, если я не включаю -ffast-math
или -funsafe-math-optimizations
.
два фрагмента кода, с которыми я играл:
tsum = 0.0d0
tvec(1:n) = A(i1:i2, ir)
do ii = 1,n
tsum = tsum + tvec(ii)
enddo
и
tsum = sum(A(i1:i2,ir))
и вот время, которое я получаю при запуске первого фрагмента кода с различными параметрами компиляции:
10.62 sec ... None
10.35 sec ... -mtune=native -mavx
7.44 sec ... -mtune-native -mavx -ffast-math
7.49 sec ... -mtune-native -mavx -funsafe-math-optimizations
наконец, с этими же оптимизациями я могу векторизовать tsum = sum(A(i1:i2,ir))
и
7.96 sec ... None
8.41 sec ... -mtune=native -mavx
5.06 sec ... -mtune=native -mavx -ffast-math
4.97 sec ... -mtune=native -mavx -funsafe-math-optimizations
когда мы сравниваем sum
и -mtune=native -mavx
С -mtune=native -mavx -funsafe-math-optimizations
, он показывает ускорение ~70%. (Обратите внимание, что они были запущены только один раз - перед публикацией мы сделаем истинный бенчмаркинг на нескольких запусках).
Я принимаю небольшой удар, хотя. Мои значения немного меняются, когда я использую -f
параметры. Без них ошибки для моих переменных (v1
, v2
) являются :
v1 ... 5.60663e-15 9.71445e-17 1.05471e-15
v2 ... 5.11674e-14 1.79301e-14 2.58127e-15
но с оптимизациями ошибки:
v1 ... 7.11931e-15 5.39846e-15 3.33067e-16
v2 ... 1.97273e-13 6.98608e-14 2.17742e-14
что указывает на то, что действительно есть что-то другое.
ваша явная версия цикла по-прежнему добавляет FP в другом порядке, чем векторизованная версия. Векторная версия использует 4 аккумулятора, каждый из которых получает каждый 4-й элемент массива.
вы можете написать свой исходный код, чтобы соответствовать тому, что будет делать векторная версия:
tsum0 = 0.0d0
tsum1 = 0.0d0
tsum2 = 0.0d0
tsum3 = 0.0d0
tn = i2 - i1 + 1
tvec(1:tn) = A(i1:i2, ir)
do ii = 1,tn,4 ! count by 4
tsum0 = tsum0 + tvec(ii)
tsum1 = tsum1 + tvec(ii+1)
tsum2 = tsum2 + tvec(ii+2)
tsum3 = tsum3 + tvec(ii+3)
enddo
tsum = (tsum0 + tsum1) + (tsum2 + tsum3)
этой может векторизовать без -ffast-math
.
FP add имеет многоцикловую задержку, но один или два на пропускную способность часов, поэтому вам нужен asm для использования нескольких векторов аккумуляторы для насыщения FP добавить блок(ы). Skylake может делать два добавления FP за часы с задержкой=4. Предыдущие процессоры Intel делают по одному на часы с задержкой=3. Так на Skylake, вам нужно 8 аккумуляторов вектора насытить блоки FP. И, конечно, они должны быть векторами 256b, потому что инструкции AVX так же быстры, но выполняют вдвое больше работы, чем инструкции вектора SSE.
написание источника с переменными аккумулятора 8 * 8 было бы смешно, поэтому я думаю, вам нужно -ffast-math
, или OpenMP pragma, которая сообщает компилятору, что различные порядки операций в порядке.
явное развертывание источника означает, что вы должны обрабатывать количество циклов, которые не кратны ширине вектора * unroll. Если вы вводите ограничения на вещи, это может помочь компилятору избежать создания нескольких версий цикла или дополнительного кода установки/очистки цикла.