2014-09-30 2 views
0

Я написал 16 * 4 SAD-функцию и ее оптимизированную для рук версию. Неподвижная версия написана встроенной сборкой. Моя проблема в том, что я получаю оптимизацию только 2x (с включенным O3), в идеале я должен получить хотя бы 6-кратную оптимизацию. Может ли кто-нибудь объяснить внутренности происходящего?Неподвижная версия SAD 16 * 4, не дающая ожидаемого выигрыша

static unsigned int f_sad_16x4 (const unsigned char* a, const unsigned char* b, const unsigned int uiStrideOrg, const unsigned int uiStrideCur) 
{ 
    unsigned int sad = 0; 
    for (int i = 0; i < 4; i++) 
    { 
     for (int j = 0; j < 16; j++) 
     { 
      sad += abs(static_cast<int>(a[i*uiStrideOrg+j]) - static_cast<int>(b[i*uiStrideCur+j])); 
     } 
    } 
    return sad; 
} 

static unsigned int f_sad_16x4_neon(const unsigned char* a, const unsigned char* b, const unsigned int uiStrideOrg, const unsigned int uiStrideCur) 
{ 
    unsigned short auiSum[8]; 
    unsigned short* puiSum = auiSum; 

    __asm__ volatile(       \ 

    /* Load 4 rows of piOrg and piCur each */ 
    "vld1.8 {q0},[%[piOrg]],%[iStrideOrg] \n\t"\ 
    "vld1.8 {q4},[%[piCur]],%[iStrideCur] \n\t"\ 
    "vld1.8 {q1},[%[piOrg]],%[iStrideOrg] \n\t"\ 
    "vabd.u8 q8, q0, q4     \n\t"\ 
    "vld1.8 {q5},[%[piCur]],%[iStrideCur] \n\t"\ 
    "vld1.8 {q2},[%[piOrg]],%[iStrideOrg] \n\t"\ 
    "vabd.u8 q9, q1, q5     \n\t"\ 
    "vld1.8 {q6},[%[piCur]],%[iStrideCur] \n\t"\ 
    "vld1.8 {q3},[%[piOrg]],%[iStrideOrg] \n\t"\ 
    "vabd.u8 q10, q2, q6     \n\t"\ 
    "vld1.8 {q7},[%[piCur]],%[iStrideCur] \n\t"\ 
    "vpaddl.u8 q12, q8      \n\t"\ 
    "vabd.u8 q11, q3, q7     \n\t"\ 
    "vpaddl.u8 q13, q9      \n\t"\ 
    "vpaddl.u8 q14, q10      \n\t"\ 
    "vadd.u16 q8, q12, q13     \n\t"\ 
    "vpaddl.u8 q15, q11      \n\t"\ 
    "vadd.u16 q9, q14, q15     \n\t"\ 
    "vadd.u16 q0, q8, q9     \n\t"\ 
    "vst1.16 {q0}, [%[puiSum]]    \n\t"\ 
    :[piOrg]  "+r" (a), 
    [piCur]  "+r" (b), 
    [puiSum]  "+r" (puiSum) 
    :[iStrideCur] "r"  (uiStrideCur), 
    [iStrideOrg] "r"  (uiStrideOrg) 
    :"q0","q1","q2","q3","q4","q5","q6","q7","q8","q9","q10","q11","q12","q13","q14","q15" 
    ); 

    unsigned int uiSum += auiSum[0] + auiSum[1] + auiSum[2] + auiSum[3] + auiSum[4] + auiSum[5] + auiSum[6] + auiSum[7]; 


    return uiSum; 
} 
+0

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

+0

Вы проверили код сборки, который компилятор генерирует из первой функции? Возможно, он уже использует неоновый блок ... –

+0

Он не использует неоновые инструкции в неоптимизированной версии – user3249055

ответ

1

Этот код работает плохо, потому что компилятор должен излучать 23 целочисленные инструкции, в дополнении к 20 инструкции неона в вашем встроенном ассемблере блоке.

Простейшим часть, чтобы исправить это строка:

unsigned int uiSum += auiSum[0] + auiSum[1] + auiSum[2] + auiSum[3] + auiSum[4] + auiSum[5] + auiSum[6] + auiSum[7]; 

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

VADDL.S16 q0, d0, d1  // 32 bit lanes in q0 
VPADDL.S32 q0, q0   // 64 bit lanes in q0 
VADD.I64 d0, d0, d1  // one 64 bit result in d0 

Вы можете получить результат одним движением:

VMOV %n, %Hn, d0   // retrieve 64 bit result 

В выше, вам необходимо установить п переписываться соответствующий операнд для переменной результата в блоке встроенный ASM выходы ,

Другая проблема - распределение регистров является субоптимальным. Регистры d8-d15 (q4-q7) должны быть сохранены любой функцией, которая их использует, и в результате компилятор испускает код для этого. Вы можете переписать свою функцию для повторного использования регистров и не использовать эти регистры.

Эта функция выиграет от использования свойств NEON. Это позволит избежать необходимости беспокоиться о распределении регистров, а также сделает ваш код переносимым в Aarch64

+0

Я не понимал, что часть, где вы говорите, d8 - d15, должна быть сохранена. Почему это так ? – user3249055

+0

@ user3249055 http://stackoverflow.com/a/5467986/1163019 – auselen

+0

Это требование ARM ABI. Компилятор предполагает, что вызов функции не изменит значения в d8-d15, поэтому он будет хранить переменные в этих регистрах. Поэтому функция, которая использует эти регистры, должна восстановить исходные значения до того, как они вернутся, чтобы предотвратить получение неверными результатами вызывающей функции. –