Ответ 1
Скажем, там больше, чем просто пара вещей, препятствующих векторизации этого цикла...
Рассмотрим это:
int main(){
byte *source = new byte[1000];
DWORD *dest = new DWORD[1000];
for (int i = 0; i < 200; ++i) {
dest[(i*2*4096)+0] = (source[(i*8)+0]);
}
for (int i = 0; i < 200; ++i) {
dest[i*2*4096] = source[i*8];
}
for (int i = 0; i < 200; ++i) {
dest[i*8192] = source[i*8];
}
for (int i = 0; i < 200; ++i) {
dest[i] = source[i];
}
}
Выход компилятора:
main.cpp(10) : info C5002: loop not vectorized due to reason '1200'
main.cpp(13) : info C5002: loop not vectorized due to reason '1200'
main.cpp(16) : info C5002: loop not vectorized due to reason '1203'
main.cpp(19) : info C5002: loop not vectorized due to reason '1101'
Давайте сломаем это:
-
Первые две петли одинаковы. Поэтому они дают исходную причину
1200
, которая является зависимой от цикла зависимостью. -
Третий цикл совпадает с 2-м циклом. Однако компилятор дает другую причину
1203
:Тело цикла включает несмежные обращения в массив
Хорошо... Почему по другой причине? Не знаю. Но на этот раз причина верна.
-
Четвертый цикл дает
1101
:Loop содержит операцию, не подлежащую векторизации (может быть неявной)
Итак, VС++ не достаточно умен, чтобы выпустить инструкцию SSE4.1
pmovzxbd
.В этом случае, я бы не ожидал, что какой-нибудь современный компилятор сможет это сделать. И если это возможно, вам нужно будет указать SSE4.1.
Итак, единственное, что необычно, - это то, почему исходный цикл сообщает о зависящей от цикла зависимости.
Ну, это сложный вызов... Я бы зашел так далеко, чтобы сказать, что компилятор просто isn Неправильная причина. (Когда это действительно должен быть несмежный доступ.)
Возвращаясь к точке, я бы не ожидал, что MSVC или любой компилятор смогут векторизовать ваш исходный цикл. Ваш исходный цикл имеет доступ, сгруппированный в куски 4, что делает его достаточно смежным для векторизации. Но это долгий шанс ожидать, что компилятор сможет это распознать.
Итак, если это имеет значение, я предлагаю вручную векторизовать этот цикл. Для этого вам понадобится _mm_cvtepu8_epi32()
.
Ваш исходный цикл:
for (int i = 0; i < count; ++i) {
dest[(i*2*pitch)+0] = (source[(i*8)+0]);
dest[(i*2*pitch)+1] = (source[(i*8)+1]);
dest[(i*2*pitch)+2] = (source[(i*8)+2]);
dest[(i*2*pitch)+3] = (source[(i*8)+3]);
dest[((i*2+1)*pitch)+0] = (source[(i*8)+4]);
dest[((i*2+1)*pitch)+1] = (source[(i*8)+5]);
dest[((i*2+1)*pitch)+2] = (source[(i*8)+6]);
dest[((i*2+1)*pitch)+3] = (source[(i*8)+7]);
}
векторизуется следующим образом:
for (int i = 0; i < count; ++i) {
__m128i s0 = _mm_loadl_epi64((__m128i*)(source + i*8));
__m128i s1 = _mm_unpackhi_epi64(s0,s0);
*(__m128i*)(dest + (i*2 + 0)*pitch) = _mm_cvtepu8_epi32(s0);
*(__m128i*)(dest + (i*2 + 1)*pitch) = _mm_cvtepu8_epi32(s1);
}
Отказ от ответственности: это не проверено и игнорирует выравнивание.