Estoy trabajando en una aplicación de audio en el Nucleo F411RE y noté que mi procesamiento era demasiado lento, lo que hacía que la aplicación omitiera algunas muestras.
Mirando mi desmontaje, pensé que dada la cantidad de instrucciones y el reloj de CPU de 100 MHz (que configuré en STM32CubeMx), debería ser mucho más rápido.
Revisé el valor de SYSCLK y es 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 tomó 14.5 µs, es decir, 1450 ciclos de reloj. Creo que es demasiado, teniendo en cuenta 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 fase y faseInc son flotantes de precisión simple y el valor es un int16_t, sine[] es una tabla de búsqueda que contiene 1024 int16_t.
No debería ser más de 500 ciclos, ¿verdad? Miré el desmontaje, usa las instrucciones de punto flotante... Por ejemplo, el desmontaje de la última línea es: vfma.f32 => 3 ciclos vcvt.s32.f32 => 1 ciclo vstr => 2 ciclos ldrh.w = > 2 ciclos
(ciclos de tiempo de acuerdo con esto ) Así que 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.
EDITAR : Acabo de medir el tiempo línea por línea, puedes verlo en el código de arriba. Parece que la línea que consume más tiempo es la línea de la tabla de búsqueda, lo que significaría que es el tiempo de acceso a la memoria lo que es crítico. ¿Cómo podría mejorar eso?
EDIT2: desmontaje, según lo solicitado por BruceAbott (lo siento, es un poco complicado, 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>
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 hardware, por lo que estas funciones deben realizarse en software y llevará muchos ciclos ejecutarlas. Para asegurarse de que todos los cálculos se realicen en 32 bits, debe definir todos sus números de coma flotante (incluidas las constantes) como tipo float
.
jonathan rueda
Marko Bursic
Florencio
Florencio
Bence Kaulics
Florencio
Ligeramente anotado
if
declaración interna. Si eso es corto, intente colocarlo alrededor de la tabla de búsqueda de senos. Pronto...Florencio
bruce abbott
Florencio
tubo
Florencio
Florencio
membrane1.mode[i].value = sine[membrane1.mode[i].phase];
(0.96 µs). ¿Hay alguna manera de optimizar esto?Florencio
viejo contador de tiempo
Florencio
viejo contador de tiempo
viejo contador de tiempo
Miguel
Florencio
viejo contador de tiempo