Реализация контрольной суммы для Neon в Intrinsics
Я пытаюсь реализовать код вычисления контрольной суммы (дополнение 2 дополнения) для NEON, используя встроенный. Текущее вычисление контрольной суммы выполняется на ARM.
Моя реализация извлекает 128-битные данные сразу из памяти в регистры NEON и выполняет SIMD (сложение), а результат складывается в 16-битное число из 128-битного числа.
Все выглядит нормально, но моя реализация NEON отнимает больше времени, чем у версии ARM.
ARM версия занимает: 0,860000 с NEON версия занимает: 1,260000 с
Замечания:
- Профилированные с использованием утилит из "time.h"
- Функция контрольной суммы вызывается 10000 раз из примера приложения, и время вычисляется после полного запуска всех функций
Другие детали:
- Использовал цепочку инструментов GNU (arm-none-linux-gnueabi-gcc) для компиляции внутреннего кода, а не цепочку инструментов arm.
- Платформа Linux.
- С-внутренний код.
Вопросы:
Почему версия NEON занимает больше времени, чем версия ARM? (Хотя я позаботился о том, чтобы использовался внутренний с минимальным количеством циклов в партии)
Как добиться того, чего я хочу достичь? (эффективность с NEON)
Может ли кто-нибудь указать мне или поделиться некоторыми примерами реализации (псевдокод / алгоритмы / код, а не теоретические документы или доклады о реализации), в которых совместно используются взаимодействия ARM-NEON?
Любая помощь приветствуется.
Вот мой код:
uint16_t do_csum(const unsigned char * buff, int len)
{
int odd, count, i;
uint32x4_t result = veorq_u32( result, result), sum = veorq_u32( sum, sum);
uint16x4_t data, data_hi, data_low, data8;
uint16x8_t dataq;
uint16_t result16, disp[20] = {0,0,0,0,0,0,0,0,0,0};
if (len <= 0)
goto out;
odd = 1 & (unsigned long) buff;
if (odd) {
uint8x8_t data1 = veor_u8( data1, data1);
data1 = (uint16x4_t)vld1_lane_u8((uint8_t *)buff, data1, 0); //result = *buff << 8;
data1 = (uint16x4_t)vshl_n_u16( data1, 8);
len--;
buff++;
result = vaddw_u16(result, data1);
}
count = len >> 1; /* nr of 16-bit words.. */
if (count) {
if (2 & (unsigned long) buff) {
uint16x4_t data2 = veor_u16( data2, data2);
data2 = (uint16x4_t) vld1_lane_u16((uint16_t *)buff, data2, 0); //result += *(unsigned short *) buff;
count--;
len -= 2;
buff += 2;
result = vaddw_u16( result, data2);
}
count >>= 1; /* nr of 32-bit words.. */
if (count) {
if (4 & (unsigned long) buff) {
uint32x2_t data4 = (uint16x4_t) vld1_lane_u32((uint32_t *) buff, data4, 0);
count--;
len -= 4;
buff += 4;
result = vaddw_u16( result, data4);
}
count >>= 1; /* nr of 64-bit words.. */
if (count) {
if (8 & (unsigned long) buff) {
uint64x1_t data8 = vld1_u64((uint64_t *) buff);
count--;
len -= 8;
buff += 8;
result = vaddw_u16( result,(uint16x4_t)data8);
}
count >>= 1; /* nr of 128-bit words.. */
if (count) {
do {
dataq = (uint16x8_t)vld1q_u64((uint64_t *) buff); // VLD1.64 {d0, d1}, [r0]
count--;
buff += 16;
sum = vpaddlq_u16(dataq);
vst1q_u16( disp, dataq); // VST1.16 {d0, d1}, [r0]
result = vaddq_u32( sum, result);
} while (count);
}
if (len & 8) {
uint64x1_t data8 = vld1_u64((uint64_t *) buff);
buff += 8;
result = vaddw_u16( result, (uint16x4_t)data8);
}
}
if (len & 4) {
uint32x2_t data4 = veor_u32( data4, data4);
data4 = (uint16x4_t)vld1_lane_u32((uint32_t *) buff, data4, 0);//result += *(unsigned int *) buff;
buff += 4;
result = vaddw_u16( result,(uint16x4_t) data4);
}
}
if (len & 2) {
uint16x4_t data2 = veor_u16( data2, data2);
data2 = (uint16x4_t) vld1_lane_u16((uint16_t *)buff, data2, 0); //result += *(unsigned short *) buff;
buff += 2;
result = vaddw_u16( result, data2);
}
}
if (len & 1){
uint8x8_t data1 = veor_u8( data1, data1);
data1 = (uint16x4_t) vld1_lane_u8((uint8_t *)buff, data1, 0); //result = *buff << 8;
result = vaddw_u8( result, data1);
}
result16 = from128to16(result);
if (odd)
result16 = ((result16 >> 8) & 0xff) | ((result16 & 0xff) << 8);
out:
return result16;
}
1 ответ
Несколько вещей, которые вы можете улучшить:
- Избавиться от магазинов, чтобы
disp
- это похоже на отладочный код, который остался в? - Не делайте горизонтальное сложение в вашем основном цикле - просто делайте частичные (вертикальные) суммы в цикле и сделайте одно окончательное горизонтальное сложение после цикла (см. Этот ответ для примера того, как это сделать - это для SSE, но принцип таков: тот же самый)
- Убедитесь, что вы используете
gcc -O3 ...
получить максимальную выгоду от оптимизации компилятора - Не использовать
goto
! (Не влияет на производительность, но является злом.)