Я написал несколько векторных методов, которые выполняют простую математику на месте или копируют и имеют одинаковое наказание для варианта на месте.
Самое простое можно свести к следующему:
void scale(float* dst, const float* src, int count, float factor)
{
__m128 factorV = _mm_set1_ps(factorV);
for(int i = 0; i < count; i+= 4)
{
__m128 in = _mm_load_ps(src);
in = _mm_mul_ps(in, factorV);
_mm_store_ps(dst, in);
dst += 4;
src += 4;
}
}
код тестирования:
for(int i = 0; i < 1000000; i++)
{
scale(alignedMemPtrDst, alignedMemPtrSrc, 256, randomFloatAbsRange1);
}
При тестировании, то есть многократном использовании этой функции на ЖЕ буферах, я обнаружил, что, если dst и src одинаковы, скорость одинакова. Если они разные, это примерно в 70 раз быстрее. Основные циклы записываются при записи (то есть _mm_store_ps)
Интересно то же самое поведение не распространяется на сложение, то есть + = хорошо работает, только * = это проблема ..
—
На это ответили в комментариях. Это ненормально во время искусственного тестирования.
Ваш factor
производить субнормальный результат? Ненулевой, но меньше, чем FLT_MIN
? Если за пределами этого есть цикл, который повторяет один и тот же блок на месте, числа могут стать достаточно маленькими, чтобы требовать медленных передач FP.
(Оказывается, да это была проблема для ОП).
Повторное умножение на месте делает числа все меньше и меньше с коэффициентом ниже 1,0. Копирование и масштабирование в другой буфер каждый раз использует одни и те же входные данные.
Это не займет дополнительное время, чтобы произвести +-Inf
или же NaN
результат, но это делает для постепенного снижения нагрузки до субнормального по крайней мере на процессорах Intel. Это одна из причин -ffast-math
устанавливает DAZ / FTZ — сбрасывать в ноль при недостаточном расходе.
Я думаю, что я читал, что AMD не поддерживает микрокодирование FP-ассистента обработки субнормалей, но у Intel есть.
На процессорах Intel есть счетчик производительности для fp_assist.any
которая рассчитывает, когда для результата, превышающего нормальный, требуются дополнительные микрокоды для обработки специального случая. (Я думаю, что это так же навязчиво, как и для внешнего интерфейса и исполнительного директора. Однако, это определенно медленно.)
Почему icc генерирует странную сборку для простого main? (показывает, как ICC устанавливает FTZ / DAZ в начале main
, с настройкой быстрой математики по умолчанию.)
Других решений пока нет …