STM32F4: Instrucciones de punto flotante demasiado lentas

6

Estoy trabajando en una aplicación de audio en el Nucleo F411RE y he notado que mi procesamiento fue demasiado lento, por lo que la aplicación omite algunas muestras.

Al ver mi desmontaje, pensé que, dado el número de instrucciones y el reloj de la CPU de 100 MHz (que he configurado en STM32CubeMx), debería ser mucho más rápido.

Verifiqué el valor de SYSCLK y es de 100Mhz como se esperaba. Para estar 100% seguro, puse 1000 "nop" en mi bucle principal y medí 10 µs, lo que corresponde a un reloj de 100 MHz.

Medí exactamente el tiempo que tomó mi procesamiento y toma 14.5 µs, es decir, 1450 ciclos de reloj. Creo que es demasiado, considerando que el procesamiento es bastante simple:

for(i=0; i<12; i++)
{
    el1.mode[i].phase += el1.osc[i].phaseInc;  // 16 µs
    if(el1.osc[i].phase >= 1.0) // 20 µs (for the whole "if"
        el1.osc[i].phase -= 1.0; 
    el1.osc[i].value = sine[ (int16_t)(el1.osc[i].phase * RES) ]; // 96 µs
    el1.val += el1.osc[i].value * el1.osc[i].amp; // 28 µs
} // that's a total of 1.63 µs for the whole loop

donde phase y phaseInc son flotadores de precisión simple, y value es un int16_t, sine [] es una tabla de consulta que contiene 1024 int16_t .

No debería ser más que como 500 ciclos, ¿verdad? Miré el desmontaje, usa las instrucciones de punto flotante ... Por ejemplo, el último desmontaje de la línea es:     vfma.f32 = > 3 ciclos     vcvt.s32.f32 = > 1 ciclo     vstr = > 2 ciclos     ldrh.w = > 2 ciclos

(ciclos de tiempo según esto ) Eso es un total de 8 instrucciones para esa línea, que es la "más grande". Realmente no entiendo por qué es tan lento ... Tal vez porque estoy usando estructuras o algo así.

Si alguien tiene una idea, me encantaría escucharla.

EDIT : acabo de medir la línea de tiempo por línea, puedes verlo en el código anterior. Parece que la línea que más tiempo consume es la línea de la tabla de consulta, lo que significa que el tiempo de acceso a la memoria es crítico. ¿Cómo podría mejorar eso?

EDIT2: desmontaje, según lo solicitado por BruceAbott (lo siento, es un poco desordenado, probablemente debido a la forma en que fue optimizado por el compilador):

membrane1.mode[i].phase += membrane1.mode[i].phaseInc;
0800192e:   vldr s14, [r5, #12]
08001932:   vldr s15, [r5, #8]
08001936:   vadd.f32 s15, s15, s14
0800193a:   adds r5, #24
179 if(membrane1.mode[i].phase >= 1.0)
0800193c:   vcmpe.f32 s15, s16
08001940:   vmrs APSR_nzcv, fpscr
180 membrane1.mode[i].phase -= 1.0;
08001944:   itt ge
08001946:   vmovge.f32 s14, #112    ; 0x70
0800194a:   vsubge.f32 s15, s15, s14
0800194e:   vstr s15, [r5, #-16]
182 membrane1.mode[i].value = sine[(int16_t)(membrane1.mode[i].phase * RES)];
08001952:   ldr.w r0, [r5, #-16]
08001956:   bl 0x80004bc <__extendsfdf2>
0800195a:   ldr r3, [pc, #112]      ; (0x80019cc <main+428>)
0800195c:   movs r2, #0
0800195e:   bl 0x8000564 <__muldf3>
08001962:   bl 0x8000988 <__fixdfsi>
08001966:   ldr r3, [pc, #104]      ; (0x80019d0 <main+432>)
184 membrane1.val += membrane1.mode[i].value * membrane1.mode[i].amp;
08001968:   vldr s13, [r5, #-4]
182 membrane1.mode[i].value = sine[(int16_t)(membrane1.mode[i].phase * RES)];
0800196c:   sxth r0, r0
0800196e:   ldrh.w r3, [r3, r0, lsl #1]
08001972:   strh.w r3, [r5, #-8]
184 membrane1.val += membrane1.mode[i].value * membrane1.mode[i].amp;
08001976:   sxth r3, r3
08001978:   vmov s15, r3
0800197c:   sxth r3, r4
0800197e:   vcvt.f32.s32 s14, s15
08001982:   vmov s15, r3
08001986:   vcvt.f32.s32 s15, s15
174 for(i=0; i<12; i++) // VADD.F32 : 1 cycle
0800198a:   cmp r5, r6
184 membrane1.val += membrane1.mode[i].value * membrane1.mode[i].amp;
0800198c:   vfma.f32 s15, s14, s13
08001990:   vcvt.s32.f32 s15, s15
08001994:   vstr s15, [sp, #4]
08001998:   ldrh.w r4, [sp, #4]
0800199c:   bne.n 0x800192e <main+270>
    
pregunta Florent

1 respuesta

15

En su desmontaje vemos llamadas a funciones matemáticas de 64 bits (doble precisión): -

08001956:   bl 0x80004bc <__extendsfdf2>
...
0800195e:   bl 0x8000564 <__muldf3>
08001962:   bl 0x8000988 <__fixdfsi>

El STM32F4 solo admite punto flotante de 32 bits en el hardware, por lo que estas funciones deben realizarse en el software y demorarán muchos ciclos en ejecutarse. Para asegurarse de que todos los cálculos se realicen en 32 bits, debe definir todos sus números de punto flotante (incluidas las constantes) como tipo float .

    
respondido por el Bruce Abbott

Lea otras preguntas en las etiquetas