ARM CMSIS DSP biquad Q31 vs F32 performance - Page 1
Forum section: electronics / general

author: bobaruni
author_rank: Regular Contributor
timestamp: May 02, 2017, 12:30:31 pm
content: Hi All,I have put together an STM32F4 based USB host audio player (FLAC, WAV + more CODECs later) that feeds into an ES9018K2M DAC via I2S.I am currently using Q31 biquads (arm_biquad_cas_df1_32x64_q31 and arm_biquad_cascade_df1_q31) for a 5 band EQ and am quickly running out of CPU time to do much else (player stutters badly when debug is enabled).Source data can be 16-24 bit stereo at up to 192K.As I'm new to CMSIS DSP and not finding much performance info on the web apart from thishttps://community.nxp.com/thread/327833, Just trying to get an idea of other people's experience with:1. Which data type should take less time on an STM32F4 with built in FPU, Q31 of F32?2. Are there any advantages in terms of precision, S/N ratio, THD, DNR etc by using Q31 or F32?Thanks in advance!

author: blkdev2
author_rank: Newbie
timestamp: May 07, 2017, 11:41:53 pm
content: Have you seen this app note from ST?AN4814Seems to be negligible difference in speed between fixed and float on the F4.

author: bobaruni
author_rank: Regular Contributor
timestamp: May 08, 2017, 04:51:44 am
content: Awesome, thanks!.So it seems F32 is very slightly faster than Q31 on FIR, looks promising so I will change the code for F32 and profile to see how much better it is in my application.

author: ehughes
author_rank: Frequent Contributor
timestamp: May 11, 2017, 08:56:43 pm
content: With q31, GCC needs to be be at -02 or -O3 optimization levels to get the integer DSP instructions. -O0 effectively puts the chip into "m3" mode.The high precision biquad function with q31 is nice as it uses the SMLAL instruction (at the correct optimization levels). This means you have 64-bit precision in the state variables and accumulator.https://github.com/ehughes/ESC-M4Goto the ESC 2017 branch.I have quite a bit of testing on the M4/M7.Some Highlights:1.) Use f32 over Q31 if you have the floating point unit, it is always faster. If you want integer and speed, use q152.) Compile with hard ABI, especially if you are function call heavy.3.) GCC needs to be at -02 or -03 for DSP code4.) GCC beats Keil in about 80% of test cases of the CMSIS library when comparing speed (clock cycles).To see the effect of the opimization level on integers:(The rand() and Printfs are nice markers)Code:#include "arm_math.h"#include "stdio.h"#include "stdlib.h"int main(){q63_t a=0;q31_t b=0;q31_t c=0;for(int i =0 ; i< 64 ; i++){b = rand();c = rand();a+=(q63_t)b*c;}//Need this so the compiler Generates what we need!printf("%ll",a);return 0;}-O01a0003e0 <main>:1a0003e0:b5b0push{r4, r5, r7, lr}1a0003e2:b086subsp, #241a0003e4:af00addr7, sp, #01a0003e6:f04f 0300mov.wr3, #01a0003ea:f04f 0400mov.wr4, #01a0003ee:e9c7 3404strdr3, r4, [r7, #16]1a0003f2:2300movsr3, #01a0003f4:60bbstrr3, [r7, #8]1a0003f6:2300movsr3, #01a0003f8:607bstrr3, [r7, #4]1a0003fa:2300movsr3, #01a0003fc:60fbstrr3, [r7, #12]1a0003fe:e01fb.n1a000440 <main+0x60>1a000400:f001 fc94bl1a001d2c <rand>1a000404:60b8strr0, [r7, #8]1a000406:f001 fc91bl1a001d2c <rand>1a00040a:6078strr0, [r7, #4]1a00040c:68bbldrr3, [r7, #8]1a00040e:4619movr1, r31a000410:ea4f 72e1mov.wr2, r1, asr #311a000414:687bldrr3, [r7, #4]1a000416:ea4f 74e3mov.wr4, r3, asr #311a00041a:fb03 f502mul.wr5, r3, r21a00041e:fb01 f004mul.wr0, r1, r41a000422:4428addr0, r51a000424:fba1 3403umullr3, r4, r1, r31a000428:1902addsr2, r0, r41a00042a:4614movr4, r21a00042c:e9d7 1204ldrdr1, r2, [r7, #16]1a000430:185baddsr3, r3, r11a000432:eb44 0402adc.wr4, r4, r21a000436:e9c7 3404strdr3, r4, [r7, #16]1a00043a:68fbldrr3, [r7, #12]1a00043c:3301addsr3, #11a00043e:60fbstrr3, [r7, #12]1a000440:68fbldrr3, [r7, #12]1a000442:2b3fcmpr3, #63; 0x3f1a000444:dddcble.n1a000400 <main+0x20>1a000446:e9d7 2304ldrdr2, r3, [r7, #16]1a00044a:4804ldrr0, [pc, #16]; (1a00045c <main+0x7c>)1a00044c:f000 fd2ebl1a000eac <printf>-O31a0003fc <main>:1a0003fc:b5f8push{r3, r4, r5, r6, r7, lr}1a0003fe:2440movsr4, #64; 0x401a000400:2600movsr6, #01a000402:2700movsr7, #01a000404:f001 fc76bl1a001cf4 <rand>1a000408:4605movr5, r01a00040a:f001 fc73bl1a001cf4 <rand>1a00040e:3c01subsr4, #11a000410:fbc0 6705smlalr6, r7, r0, r51a000414:d1f6bne.n1a000404 <main+0x8>1a000416:4632movr2, r61a000418:463bmovr3, r71a00041a:4802ldrr0, [pc, #8]; (1a000424 <main+0x28>)1a00041c:f000 fd2abl1a000e74 <printf>

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: May 11, 2017, 11:36:18 pm
content: Quote from: ehughes on May 11, 2017, 08:56:43 pmWhich Keil? The old compiler or the new one that's based on Clang/LLVM?

author: ehughes
author_rank: Frequent Contributor
timestamp: May 12, 2017, 10:05:11 pm
content: 5.06 ( this is what 99 percent of what existing customers are running)I will be running tests eventually with LLVM once it is a bit more stable. The linker and other binary tools aren't quite ready. I have seen people generate object code with LLVM and then use GCC ld. The end to end toolchain was a bit rough last I checked (about 6 months ago). Maybe it is time to look again.

author: bobaruni
author_rank: Regular Contributor
timestamp: May 22, 2017, 03:48:58 pm
content: Ok, finally got some time to profile the difference between Q31 and F32 biquads.Test was using GCC bare metal (EmBitz 1.10) with __FPU_PRESENT defined on STM32F407 @ 168MHZ.Final input and output buffers are int32_t.Even though there is conversion to and from float when using F32 biquads and no conversion when using Q31 biquads (only casting):Overall, F32 takes 20% less clock cycles (therefore less time) than Q31.I expected fixed point to be faster but the FPU hardware really helps.Got to be pretty happy with that, thanks very much for the tips guys

author: bobaruni
author_rank: Regular Contributor
timestamp: May 23, 2017, 03:19:45 pm
content: Update!By changing the loop that unpacks interleaved int32 sample data to also perform conversion to float and scale all at the same time, overall, float is now more than 50% faster than q31 with my biquads.arm_q31_to_float, arm_scale_f32 and arm_float_to_q31 are no longer required.Code:// Unpack interleaved L and R samples, convert to float32 and scale 0.5:int32_t *AudBuffOut = AudBuffIn;uint32_t s;for (s = 0; s < BLOCKSIZE; s++){outputF32L = (float32_t)*AudBuffIn++ / 4294967296;outputF32R = (float32_t)*AudBuffIn++ / 4294967296;}//DSP code here// Convert float32 to int32 and interleave samples so that they are formatted for DMA to I2s transfer:for (s = 0; s < BLOCKSIZE; s++){*AudBuffOut++ = outputF32L * 2147483648;*AudBuffOut++ = outputF32R * 2147483648;}