С отключенной оптимизацией (по умолчанию gcc -O0
), встроенные функции часто бывают ужасными. Антиоптимизированный -O0
code-gen для встроенных функций обычно причиняет больший ущерб (даже больше, чем для скалярных), а некоторые из функционально-подобных встроенных функций приводят к дополнительным затратам на хранение / перезагрузку. Кроме того, дополнительная задержка переадресации в магазине -O0
имеет тенденцию причинять больше вреда, потому что при работе с 1 вектором вместо 4 скаляров меньше ILP.
Использование gcc -march=native -O3
Но даже с включенной оптимизацией ваш код по-прежнему написан для снижения производительности SIMD путем горизонтального сложения каждого вектора внутри цикла. См. Как вычислить произведение векторной точки с использованием встроенных функций SSE в C , чтобы узнать, как не сделать это: используйте _mm_add_ps
для накопления вектора __m128 total
и только горизонтальной суммируйте его снаружицикл.
Вы ограничиваете цикл на задержке добавления FP, выполняя скалярное total +=
внутри цикла. Эта цепочка зависимостей, переносимая циклами, означает, что ваш цикл не может работать быстрее, чем 1 float
за 4 цикла в вашей микроархитектуре, полученной из Skylake, где задержка addss
равна 4 циклам. (https://agner.org/optimize/)
Даже лучше, чем __m128 total
, используйте 4 или 8 векторов, чтобы скрыть задержку добавления FP, поэтому ваш цикл SIMD может стать узким местом при пропускной способности multi / add (или FMA) вместо задержки.
Как только вы исправите это, то, как @harold указывает на то, как вы используете _mm_set_ps
внутри цикла, вы получите довольно плохой ассемблер от компилятора. Это не хороший выбор внутри цикла, когда операндыне являются константами или, по крайней мере, инвариантными к циклам.
Ваш пример здесь явно искусственный, обычно вы загружаете векторы SIMD из памяти. Но если вам нужно обновить счетчик цикла в __m128
vector, вы можете использовать tmp = _mm_add_ps(tmp, _mm_set_ps(1.0, 0, 0, 0))
. Или развернуть с добавлением 1.0, 2.0, 3.0 и 4.0, чтобы зависимость, переносимая в цикле, составляла только + = 4.0 в одном элементе.
x + 0.0
- этоИдентификационная операция даже для FP (кроме, может быть, со знаком нуля), так что вы можете сделать это с другими элементами, не изменяя их.
Или для нижнего элемента вектора, вы можете использовать _mm_add_ss
(скаляр) длятолько измените его.