Эффективная неоновая реализация отсечения

Внутри цикла я должен реализовать своего рода отсечение

if ( isLast )
{
    val = ( val < 0 ) ? 0 : val;
    val = ( val > 255 ) ? 255 : val;        
}

Однако это отсечение занимает почти половину времени выполнения цикла в Neon. Вот как выглядит весь цикл:

for (row = 0; row < height; row++)
{
  for (col = 0; col < width; col++)
  {
      Int sum;
      //...Calculate the sum
   
      Short val = ( sum + offset ) >> shift;
      if ( isLast )
      {
           val = ( val < 0 ) ? 0 : val;
           val = ( val > 255 ) ? 255 : val;        
      }
      dst[col] = val;
   }

}

Так реализовано отсечение в Neon.

     cmp       %10,#1                           //if(isLast)         
     bne       3f                                         
     vmov.i32   %4, d4[0]                       //put val in %4
     cmp       %4,#0                            //if( val < 0 )
     blt       4f                               
     b         5f                               
     4:                                         
     mov       %4,#0                             
     vmov.i32   d4[0],%4                        
     5:                                         
     cmp       %4,%11                          //if( val > maxVal )
     bgt       6f                               
     b         3f                               
     6:                                         
     mov       %4,%11                            
     vmov.i32   d4[0],%4                       
     3:  

     

Это отображение переменных в регистры-

isLast-    %10
maxVal-    %11

Любые предложения, чтобы сделать это быстрее? Спасибо

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

Вырезка сейчас выглядит так:

     "cmp       %10,#1                            \n\t"//if(isLast)      
     "bne       3f                                \n\t"          
     "vmin.s32   d4,d4,d13                        \n\t"
     "vmax.s32   d4,d4,d12                        \n\t"
     "3:                                          \n\t" 

//d13 contains maxVal(255)
//d12 contains 0

Время, затрачиваемое на выполнение этой части кода, сократилось с 223 мс до 18 мс.


person iajnr    schedule 17.07.2012    source источник
comment
Вы можете рассмотреть возможность исключения условного перехода в цикле. Например, если 'isLast' не изменяется в цикле, вы можете всегда обрезать до (lo,hi), где (lo,hi) установлено заранее - (0,255), когда isLast, и (-0x80000000,0x7FFFFFFFF) в противном случае.   -  person greggo    schedule 21.01.2015


Ответы (2)


Использование обычных сравнений с NEON почти всегда является плохой идеей, потому что оно переводит содержимое регистра NEON в регистр ARM общего назначения, а это требует большого количества циклов.

Вы можете использовать инструкции vmin и vmax NEON. Вот небольшой пример, который ограничивает массив целых чисел любыми минимальными/максимальными значениями.

void clampArray (int minimum,
                 int maximum,
                 int * input,
                 int * output,
                 int numElements)
{
  // get two NEON values with your minimum and maximum in each lane:
  int32x2_t lower  = vdup_n_s32 (minimum);
  int32x2_t higher = vdup_n_s32 (maximum);
  int i;

  for (i=0; i<numElements; i+=2)
  {
    // load two integers
    int32x2_t x = vld1_s32 (&input[i]);

    // clamp against maximum:
    x = vmin_s32 (x, higher);

    // clamp against minimum
    x = vmax_s32 (x, lower);

    // store two integers
    vst1_s32 (&output[i], x);
  }
} 

Предупреждение: этот код предполагает, что numElements всегда кратно двум, и я не проверял это.

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

person Nils Pipenbrinck    schedule 17.07.2012
comment
Что ж, это уменьшило накладные расходы с 200 мс до 18 мс. Спасибо! - person iajnr; 17.07.2012

Если maxVal равен UCHAR_MAX, CHAR_MAX, SHORT_MAX или USHORT_MAX, вы можете просто преобразовать с помощью neon из int в желаемый тип данных путем приведения с насыщением.

По примеру

// Will convert four int32 values to signed short values, with saturation.
int16x4_t vqmovn_s32 (int32x4_t) 

// Converts signed short to unsgigned char, with saturation
uint8x8_t vqmovun_s16 (int16x8_t) 

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

person Sam    schedule 17.07.2012
comment
Хорошее решение - см. также vmax(q)_xxx/vmin(q)_xxx. - person Paul R; 17.07.2012
comment
maxVal равен 255. Можем ли мы как-то использовать vqshrn? - person iajnr; 17.07.2012
comment
вы можете объединить две операции приведения: vqmovun_s16(vqmovn_s32( ... )). Но ответ Нильса более гибкий и полный. - person Sam; 17.07.2012