Проделал несколько экспериментов.
1) Переписал Diff на ARM ASM:
Код
static inline int Diff(u32 yuv1,u32 yuv2)
{
register u32 t,z;
__asm
{
USUB8 t,yuv1,yuv2 // t=yuv1-yuv2
USUB8 yuv2,yuv2,yuv1 // yuv2=yuv2-yuv1
SEL t,yuv2,t // t=abs(yuv1-yuv2)
MOV yuv1,#YUVMASK // yuv1=YUVMASK
MOV z,#0 // z=0
USUB8 yuv2,yuv1,t // yuv2=YUVMASK-abs(yuv1-yuv2)
SEL yuv1,z,yuv1 // yuv1=(YUVMASK>=abs(yuv1-yuv2))?0:YUVMASK
}
return yuv1;
}
Фильтр работает, причем быстрее, чем с Си-шной реализацией Diff().
2) Сравнил быстродействие фильтра с вариантом JumpTable и Case/Switch. Как это ни странно, вариант с Case/Switch быстрее! Оптимизация Keil CC : -O3 -Otime.
Приемлемость работы фильтра оцениваю по косвенному признаку: задержки обновления кольцевого буфера DMA для воспроизведения звука - если звук хрипит, значит данные не успевают обновляться, значит фильтр нужно ещё более оптимизировать!
С фильтрами SaI, LQ2x, Scale2x звук не хрипит.
3) Возникла идея откомпилировать код фильтра на GCC и полученный объектник подстыковать к Keil. Думал, что невозможно, а оказалось - возможно!
Откомпилировал фильтр GCC тулчейн arm-eabi-gcc6.2.0-r4.exe :
Код
C:\arm-eabi\bin\arm-eabi-gcc.exe -fshort-wchar -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -O3 -Ofast -c -I ../Port HQ2x.cpp -o HQ2x.obj
Построил либу для Кейла:
Код
C:\arm-eabi\bin\arm-eabi-ar.exe cru HQ2x.lib HQ2x.obj
и подстыковал её в проект Keil.
Результаты приятно удивили - звук идёт ровно без лагов, фильтр работает!
Выходит, что GCC лучше заоптимизировал?
Ещё заметил, что ASM-листинг, даваемый компилятором Keil какой-то избыточный. А в GCC листинг достаточно прозрачен : применение инструкций вполне очевиден.
Связано ли это с тем, что Keil таблеточный? И производитель делает флуд АСМ-инструкций в генерируемый код в случае таблетки?
4) Вернул JumpTable вместо Case/Switch и скомпилировал в GCC. Результаты лучше, чем с Keil, но и тут Case/Switch также победил.
5) Заменил своппинг байтов на аппаратный:
Код
//#define rev16(x) (((x)>>8)|((x)<<8))
static u32 inline rev16(u32 x)
{
asm(
"rev16 %[value],%[value]"
: [value] "+r" (x)
:
: "cc"
);
return x;
}
Стало ещё лучше - в GCC. А вот в Keil - наоборот было хуже. Это странно.
Асм-листинг в GCC смотрю:
Код
C:\arm-eabi\bin\arm-eabi-gcc.exe -fshort-wchar -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -O3 -Ofast -S -I ../Port HQ2x.cpp -o HQ2x.S
А теперь вопросы, попрошу ответить на них:
1) Можно ли в GCC выставить ещё более сильные флаги оптимизации по скорости, кроме тех что есть -O3 - Ofast ?
2) Оправдан ли переход на более новую версию тулчейна GCC с целью получить более производительный код?
3) Может ли ядро Cortex-M7 посчитать интерполяцию быстрее. Такого типа:
Код
pixel =( (pixel1 * 3) + pixel2) / 4
где pixel - это пиксели 0:8:8:8 bit 0:R:G:B
4) Спуфит ли Keil лишними ASM-инструкциями для намерянного снижения производительности в вылеченных версиях?
5) Хочется избавиться от байт-своппинга REV16. Необходимость продиктована использованием DMA - дисплей требует занос старшего байта вначале, что приводит к некорректному отображения цвета (так как DMA передаёт вначале младший байт по дефолту).
6) В STM32H743 есть Master DMA и битовое поле Little/Big endianess для байтов/полуслов/слов. Но он у меня не заработал (трансфер Memory to Peripheral). А должен ли?
Рабочий код фильтра прикрепляю (и батник для сборки либы для Keil-ы под GCC ):
Нажмите для просмотра прикрепленного файлаЦитата(jcxz @ Jul 17 2018, 07:17)

Кстати - по этой найденной Вами ассемблерной Diff() уже видно как значительно можно выиграть, написав весь блок вызовов Diff() в виде асм-функции и заинлайнив туда саму Diff(). Из неё сразу выпадают:
Код
mov tmp2,#0 // tmp2 = 0
movw value1,#0x0706
movt value1,#0x30 // value1 = 0x300706
Которые запросто выносятся за рамки цикла. И вызовов/возвратов не нужно.
Попытаюсь осмыслить Вами сказанное и испытать.
Цитата(jcxz @ Jul 16 2018, 19:30)

По идее можно попробовать написать просто: MOV Rx, #0x300706. Такой команды конечно нет, но некоторые трансляторы умные и заменяют такое на требуемую последовательность команд, возможно - наиболее оптимальную по их мнению.
Проверил: GCC делает 2 MOV, а Keil - LDR память