Подтвердить что ты не робот

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

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

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

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

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

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

Эти указатели 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[] - это остатки вызова функции, который я упустил здесь, потому что цикл по-прежнему не будет автоматически сгенерирован без вызова функции в самой простейшей форме.

EDIT:

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

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

Ответ 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);
}

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

Ответ 2

Из документации 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.

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