Рассмотрим два следующих фрагмента кода, первый — это версия C:
void __attribute__((no_inline)) proj(uint8_t * line, uint16_t length)
{
uint16_t i;
int16_t tmp;
for(i=HPHD_MARGIN; i<length-HPHD_MARGIN; i++) {
tmp = line[i-3] - 4*line[i-2] + 5*line[i-1] - 5*line[i+1] + 4*line[i+2] - line[i+3];
hphd_temp[i]=ABS(tmp);
}
}
Вторая — та же функция (за исключением рамки), использующая неоновые встроенные функции.
void __attribute__((no_inline)) proj_neon(uint8_t * line, uint16_t length)
{
int i;
uint8x8_t b0b7, b8b15, p1p8,p2p9,p4p11,p5p12,p6p13, m4, m5;
uint16x8_t result;
m4 = vdup_n_u8(4);
m5 = vdup_n_u8(5);
b0b7 = vld1_u8(line);
for(i = 0; i < length - 16; i+=8) {
b8b15 = vld1_u8(line + i + 8);
p1p8 = vext_u8(b0b7,b8b15, 1);
p2p9 = vext_u8(b0b7,b8b15, 2);
p4p11 = vext_u8(b0b7,b8b15, 4);
p5p12 = vext_u8(b0b7,b8b15, 5);
p6p13 = vext_u8(b0b7,b8b15, 6);
result = vsubl_u8(b0b7, p6p13); //p[-3]
result = vmlal_u8(result, p2p9, m5); // +5 * p[-1];
result = vmlal_u8(result, p5p12, m4);// +4 * p[1];
result = vmlsl_u8(result, p1p8, m4); //-4 * p[-2];
result = vmlsl_u8(result, p4p11, m5);// -5 * p[1];
vst1q_s16(hphd_temp + i + 3, vabsq_s16(vreinterpretq_s16_u16(result)));
b0b7 = b8b15;
}
/* todo : remaining pixel */
}
Я разочарован приростом производительности: он составляет около 10-15%. Если я посмотрю на сгенерированную сборку:
- Версия C преобразована в цикл из 108 инструкций.
- Неоновая версия трансформируется в цикл из 72 инструкций.
Но один цикл в неоновом коде вычисляет в 8 раз больше данных, чем итерация в цикле C, так что должно быть заметно значительное улучшение.
Есть ли у вас какое-либо объяснение относительно небольшой разницы между двумя версиями?
Дополнительные сведения: Тестовые данные представляют собой изображение с разрешением 10 Мпикс, время расчета составляет около 2 секунд для версии C.
Процессор: ARM Cortex A8
tmp = 1*(line[i-3] - line[i+3]) + 4*(line[i+2] - line[i-2]) + 5*(line[i-1] - line[i+1])
, а просто вычисляете три разности плюс скалярное произведение в конце (это будет другая последовательность сбора-нагрузки/векторной подпрограммы/параллельного умножения). Но это поможет только в том случае, если вы уже не ограничены пропускной способностью памяти. Было бы целесообразно выдать__pld(*(line + 8))
перед циклом и__pld(*(line + i + 16))
внутри цикла. - person FrankH.   schedule 11.06.2013