У меня есть этот код:
constexpr size_t S = 4;
void add(std::array<float, S>& a, std::array<float, S> b)
{
for (size_t i = 0; i < S; ++i)
a[i] += b[i];
}
Оба, clang и gcc, понимают, что вместо выполнения 4-х отдельных дополнений они могут сделать одно упакованное дополнение, используя инструкцию addps
. Например, clang генерирует это:
movups xmm2, xmmword ptr [rdi]
movlhps xmm0, xmm1 # xmm0 = xmm0[0],xmm1[0]
addps xmm0, xmm2
movups xmmword ptr [rdi], xmm0
ret
Как вы можете видеть на godbolt , gcc немного отстает от clang, так как ему нужно больше ходов. Но это нормально. Моя проблема в msvc, которая намного хуже, как вы можете видеть:
mov eax, DWORD PTR _a$[esp-4]
movups xmm2, XMMWORD PTR _b$[esp-4]
movss xmm1, DWORD PTR [eax+4]
movaps xmm0, xmm2
addss xmm0, DWORD PTR [eax]
movss DWORD PTR [eax], xmm0
movaps xmm0, xmm2
shufps xmm0, xmm2, 85 ; 00000055H
addss xmm1, xmm0
movaps xmm0, xmm2
shufps xmm0, xmm2, 170 ; 000000aaH
shufps xmm2, xmm2, 255 ; 000000ffH
movss DWORD PTR [eax+4], xmm1
movss xmm1, DWORD PTR [eax+8]
addss xmm1, xmm0
movss xmm0, DWORD PTR [eax+12]
addss xmm0, xmm2
movss DWORD PTR [eax+8], xmm1
movss DWORD PTR [eax+12], xmm0
ret 0
Я пробовал разные уровни оптимизации, но /O2
кажется лучшим. Я также попытался вручную развернуть цикл, но без изменений для msvc.
Итак, есть ли способ заставить msvc выполнить такую же оптимизацию, используя один addps
вместо четырех addss
? Или, может быть, есть веская причина, по которой msvc этого не делает?
Редактировать
Добавляя флаг /Qvec-report:2
, как предложил Шон в комментариях (спасибо!) Я обнаружил, что msvc считает, что цикл слишком мал, чтобы извлечь какую-то выгоду из его векторизации. У Clang и GCC разные мнения, но все в порядке. И действительно, если я изменю S
на 16
, msvc создаст векторизованную версию, даже несмотря на то, что она все еще обеспечивает не векторизованную ветвь (на мой взгляд, совершенно ненужную, поскольку S
известна во время компиляции). В целом, код msvc выглядит как беспорядок по сравнению с gcc и clang, см. здесь .