Цикл оптимизации с несколькими инструкциями (SSE2, SSE4) с TBB

У меня есть простой алгоритм обработки изображений. Вкратце, изображение (среднее) в формате с плавающей запятой вычитается из 8-битного изображения, а затем результат сохраняется в изображение с плавающей запятой (назначение).

эта функция в основном написана встроенными функциями.

Я попытался оптимизировать эту функцию с помощью TBB, parrallel_for, но не получил прибавки в скорости, но получил штраф.

Что я должен делать ? Должен ли я использовать более низкоуровневую схему, такую ​​​​как задача TBB, для оптимизации кода?

float           *m, **m_data,
                *o, **o_data;
unsigned char   *p, **src_data;
register unsigned long len, i;
unsigned long   nr,
                nc;

src_data    =   src->UByteData;    // 2d array
m_data      =   mean->FloatData;   // 2d array
o_data      =   dest->FloatData;   // 2d array
nr          =   src->Rows;
nc          =   src->Cols;

__m128i xmm0;

for(i=0; i<nr; i++)
{
    m = m_data[i];
    o = o_data[i];
    p = src_data[i];
    len = nc;
    do
    {
        _mm_prefetch((const char *)(p + 16),  _MM_HINT_NTA);
        _mm_prefetch((const char *)(m + 16),  _MM_HINT_NTA);

        xmm0 = _mm_load_si128((__m128i *) (p));

        _mm_stream_ps(
                        o,
                        _mm_sub_ps(
                                    _mm_cvtepi32_ps(_mm_cvtepu8_epi32(_mm_srli_si128(xmm0, 0))),
                                    _mm_load_ps(m + offset)
                                )
                    );
        _mm_stream_ps(
                        o + 4,
                        _mm_sub_ps(
                                    _mm_cvtepi32_ps(_mm_cvtepu8_epi32(_mm_srli_si128(xmm0, 4))),
                                    _mm_load_ps(m + offset + 4)
                                )
                    );
        _mm_stream_ps(
                        o + 8,
                        _mm_sub_ps(
                                    _mm_cvtepi32_ps(_mm_cvtepu8_epi32(_mm_srli_si128(xmm0, 8))),
                                    _mm_load_ps(m + offset + 8)
                                )
                    );
        _mm_stream_ps(
                        o + 12,
                        _mm_sub_ps(
                                    _mm_cvtepi32_ps(_mm_cvtepu8_epi32(_mm_srli_si128(xmm0, 12))),
                                    _mm_load_ps(m + offset + 12)
                                )
                    );

        p += 16;
        m += 16;
        o += 16;
        len -= 16;
    }
    while(len);
}

person prgbenz    schedule 10.02.2011    source источник
comment
Не удивлюсь, если в IPP уже есть функция для этого.   -  person Anon.    schedule 10.02.2011
comment
Если вы используете компилятор Intel, почему бы просто не написать наивную версию функции и посмотреть, сможет ли компилятор сам ее векторизовать? Я не знаю о GCC в этом отношении.   -  person Jeremiah Willcock    schedule 10.02.2011


Ответы (1)


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

Однако я бы избавился от инструкций _mm_prefetch — они почти наверняка здесь не помогают и могут даже снижать производительность.

Если возможно, вы должны комбинировать этот цикл с любыми другими операциями, которые вы выполняете до/после этого - таким образом вы амортизируете стоимость ввода-вывода в память по сравнению с дополнительными вычислениями.

person Paul R    schedule 10.02.2011
comment
почему _mm_prefetch в этом случае бесполезен? - person prgbenz; 10.02.2011
comment
@prgbenz: _mm_prefetch необходимо запускать за пару сотен тактов до любого потенциального промаха кеша, и помогает только в том случае, если у вас есть лишняя пропускная способность — установка его непосредственно перед соответствующими загрузками не поможет и может даже повлиять на любая автоматическая предварительная выборка, которую ЦП уже может выполнять. Вдобавок ко всему, он тратит циклы в том, что уже является довольно коротким циклом. - person Paul R; 10.02.2011