Могут ли современные компиляторы С++ автоматизировать код для 24-битной обработки изображений?

У компиляторов, таких как gcc, visual studio С++, компилятор intel С++, clang и т.д., векторизовать код следующим образом?

std::vector<unsigned char> img( height * width * 3 );
unsigned char channelMultiplier[3];

// ... initialize img and channelMultiplier ...

for ( int y = 0; y < height; ++y )
    for ( int x = 0; x < width; ++x )
        for ( b = 0; b < 3; ++b )
            img[ b+3*(x+width*y) ] = img[ b+3*(x+width*y) ] * 
                                     channelMultiplier[b] / 0x100;

Как насчет того же для 32-битной обработки изображений?

4
задан Ralph Tandetzky 11 авг. '12 в 12:30
источник поделиться
1 ответ

Я не думаю, что ваш тройной цикл будет автоматически прорисовываться. ИМО - проблемы:

  • Доступ к памяти осуществляется через тип объекта std::vector. AFAIK Я не думаю, что какой-либо компилятор будет автоматически векторизовать код std::vector, если только операторы доступа [] или() не будут встраиваться, но все же, мне не ясно, что он будет автоматически сгенерирован.
  • Ваш код страдает от сглаживания памяти, т.е. компилятор не знает, обращается ли к памяти, к которой вы обращаетесь к img, из другого указателя памяти, и это, скорее всего, заблокирует векторизация. В основном вам нужно определить простой двойной массив и намекнуть компилятору, что ни один другой указатель не ссылается на это же место. Я думаю, вы можете сделать это, используя __restrict. __restrict сообщает компилятору, что этот указатель является единственным указателем, указывающим на эту ячейку памяти, и что нет других указателей, и, следовательно, нет никаких побочных эффектов.
  • Память не выровнена по умолчанию, и даже если компилятор управляет авто-векторией, векторизация неизмененной памяти намного медленнее, чем у выровненной памяти. Вам необходимо убедиться, что в вашей памяти 32 адреса памяти, выровненных для использования автоматической векторизации, и AVX до максимального и 16-разрядного адреса, выровненных для использования SSE, до максимума, то есть всегда выровняйте 32 адреса бит памяти. Это можно сделать динамически с помощью:

    double* buffer = NULL;
    posix_memalign((void**) &buffer, 32, size*sizeof(double));
    ...
    free(buffer);
    

в MSVC вы можете сделать это с помощью __declspec(align(32)) double array[size], но вам нужно проверить с помощью конкретного компилятора, который вы используете, чтобы убедиться, что вы используете правильные директивы выравнивания.

Еще одна важная вещь: если вы используете компилятор GNU, используйте флаг -ftree-vectorizer-verbose=6, чтобы проверить, является ли ваш цикл авто-векторией. Если вы используете компилятор Intel, используйте -vec-report5. Обратите внимание, что существует несколько уровней многословности и вывода информации, то есть номера 6 и 5, поэтому проверяйте документацию компилятора. Чем выше уровень многословия, тем больше информации о векторизации вы получите для каждого цикла в вашем коде, но медленнее компилятор будет компилироваться в режиме Release.

В общем, я всегда удивлялся тому, как НЕ легко заставить компилятор авто-векторизовать, распространенной ошибкой считается, что, поскольку цикл выглядит каноническим, тогда компилятор автоматически автогенизирует его автоматически.

UPDATE: и еще одна вещь, убедитесь, что ваш img на самом деле выровнен по странице posix_memalign((void**) &buffer, sysconf(_SC_PAGESIZE), size*sizeof(double)); (что подразумевает выравнивание AVX и SSE). Проблема в том, что если у вас есть большое изображение, этот цикл, скорее всего, приведет к переходу страницы во время выполнения, а это тоже очень дорого. Я думаю, что это так называемый TLB пропускает.

7
ответ дан Giovanni Azua 11 авг. '12 в 13:54
источник поделиться

Другие вопросы по меткам или Задайте вопрос