Где здесь циклическая зависимость?

Кто-нибудь видит что-то очевидное в коде цикла ниже, что я не вижу, почему это не может быть автоматически векторизовано компилятором C ++ VS2012?

Все компилятор дает мне это info C5002: loop not vectorized due to reason '1200' когда я использую /Qvec-report:2 переключатель командной строки.

Причина 1200 задокументирована в MSDN как:

Цикл содержит переносимые в петлю зависимости данных, которые предотвращают
векторизации. Различные итерации цикла мешают каждому
другой такой, что векторизация цикла будет давать неправильные ответы, и
авто-векторизатор не может доказать себе, что таких данных нет
зависимости.

Я знаю (или я почти уверен), что нет никаких зависимостей от переносимых данных, но я не уверен, что мешает компилятору реализовать это.

Эти source а также dest указатели никогда не перекрывают и не дублируют одну и ту же память, и я пытаюсь предоставить компилятору эту подсказку через __restrict,

pitch всегда положительное целочисленное значение, что-то вроде 4096в зависимости от разрешения экрана, поскольку это функция рендеринга / преобразования 8bpp-> 32bpp, работающая по столбцам.

byte  * __restrict source;
DWORD * __restrict dest;
int pitch;

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]);
}

Парень вокруг каждого source[] являются остатками вызова функции, который я здесь исключил, потому что цикл по-прежнему не будет автоматически векторизован без вызова функции в его самой простой форме.

РЕДАКТИРОВАТЬ:

Я упростил цикл до самой тривиальной формы, которую я могу:

for (int i = 0; i < 200; ++i) {
dest[(i*2*4096)+0] = (source[(i*8)+0]);
}

Это все еще производит тот же код причины 1200.

РЕДАКТИРОВАТЬ (2):

Этот минимальный тестовый пример с локальными распределениями и идентичными типами указателей все еще не может автоматически векторизоваться. Я просто сбит с толку в этот момент.

const byte * __restrict source;
byte * __restrict dest;
source = (const byte * __restrict ) new byte[1600];
dest = (byte * __restrict ) new byte[1600];
for (int i = 0; i < 200; ++i) {
dest[(i*2*4096)+0] = (source[(i*8)+0]);
}

12

Решение

Скажем так, есть нечто большее, чем просто пара вещей, препятствующих векторизации этого цикла …

Учти это:

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'

Давайте разберем это:

  1. Первые две петли одинаковы. Таким образом, они дают оригинальную причину 1200 которая является циклической зависимостью.

  2. 3-й цикл такой же, как 2-й цикл. Тем не менее, компилятор дает другую причину 1203:

    Тело цикла включает несмежные обращения в массив

    Хорошо … Почему другая причина? Я не знаю. Но на этот раз причина верна.

  3. 4-й цикл дает 1101:

    Цикл содержит не векторизуемую операцию преобразования (может быть неявной)

    Так что VC ++ не настолько умен, чтобы выпускать SSE4.1 pmovzxbd инструкция.

    Это довольно нишевый случай, я бы не ожидал, что какой-либо современный компилятор сможет это сделать. И если это возможно, вам нужно будет указать SSE4.1.


Так что единственное, что необычно, это то, почему начальный цикл сообщает о переносимой в цикле зависимости.
Ну, это сложный вызов … Я бы сказал, что компилятор просто не выдает правильную причину. (Когда это действительно должен быть несмежный доступ.)

Возвращаясь к делу, я бы не ожидал, что 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);
}

Отказ от ответственности: это не проверено и игнорирует выравнивание.

11

Другие решения

Из документации MSDN, случай, в котором будет сообщено об ошибке 1203

void code_1203(int *A)
{
// Code 1203 is emitted when non-vectorizable memory references
// are present in the loop body. Vectorization of some non-contiguous
// memory access is supported - for example, the gather/scatter pattern.

for (int i=0; i<1000; ++i)
{
A[i] += A[0] + 1;       // constant memory access not vectorized
A[i] += A[i*2+2] + 2;  // non-contiguous memory access not vectorized
}
}

Это действительно могут быть вычисления по индексам, которые связываются с авто-векторизатором. Забавно, но код ошибки не 1203.

MSDN Parallelizer и Vectorizer Сообщения

2

По вопросам рекламы [email protected]