stm32. Miramos la raíz

En lugar de presentar



El artículo contiene un ejemplo de optimización manual de una sección crítica de un programa de aplicación aplicado a microcontroladores stm32 de presupuesto, que aumenta el rendimiento 5 veces o más en comparación con una función de biblioteca.



La extracción de raíz cuadrada se usa a menudo en aplicaciones. La función sqrt está incluida en la biblioteca C estándar y opera con números reales:



double sqrt (double num);
long double sqrtl (long double num);


Los microcontroladores funcionan principalmente con números enteros; por lo general, no tienen registros para números reales.



En la práctica, además de la pérdida de velocidad computacional en múltiples transformaciones "entero <=> real" , también se pierde precisión - Ejemplo 1.



Ejemplo 1: pérdida de precisión en conversiones hacia adelante y hacia atrás



//  
uint32_t L1 = 169;
uint32_t L2 = 168;

//  
uint32_t r1 = ( uint32_t )sqrt( ( double ) L1 );
uint32_t r2 = ( uint32_t )sqrt( ( double ) L2 );

//  
L1 = r1*r1; // r1 = 13
L2 = r2*r2; // r2 = 12

//  
// L1 = 169 —  169
// L2 = 144 —  168,    14%


Formulación del problema



Aumente la precisión de los cálculos de sqrt redondeando al entero más cercano.

Si es posible, aumente la productividad.



La solucion del problema



Cree una función personalizada, por ejemplo, sqrt_fpu basada en la estándar - Ejemplo 2.



Ejemplo 2: cálculo de una raíz entera con el algoritmo sqrt_fpu



uint16_t sqrt_fpu ( uint32_t L )
{
    if ( L < 2 )
        return ( uint16_t ) L;

    double f_rslt = sqrt( ( double ) L );
    uint32_t rslt = ( uint32_t ) f_rslt;

    if ( !( f_rslt - ( double ) rslt < .5 ) )
        rslt++;

    return ( uint16_t ) rslt;
} 


Ventajas de sqrt_fpu:



  • código compacto;
  • se logra la precisión requerida.


Desventajas de sqrt_fpu:



  • pérdida de rendimiento debido a una llamada adicional y operaciones de punto flotante adicionales;
  • falta de potencial evidente para optimizar la velocidad de cálculo a nivel de usuario.


sqrt_fpu .



— - ().



-: , .



1. :



« , , .»

sqrt_odd — 3.



3: sqrt_odd



uint16_t sqrt_odd ( uint32_t L )
{
    if ( L < 2 )
        return ( uint16_t ) L;

    uint16_t div = 1, rslt = 1;

    while ( 1 )
    {
        div += 2;

        if ( ( uint32_t ) div >= L )
            return rslt;

        L -= div, rslt++;
    }
}


,

.



sqrt_odd:



  • ;


sqrt_odd:



  • ;
  • ; , 10e4+ 150 — 1;
  • .


1: sqrt_odd



2. :



« »:

Rj = ( N / Ri + Ri ) / 2

sqrt_new — 4.



4: sqrt_new



uint16_t sqrt_new ( uint32_t L )
{
    if ( L < 2 )
        return ( uint16_t ) L;

    uint32_t rslt, div;

    rslt = L;
    div = L / 2;

    while ( 1 )
    {
        div = ( L / div + div ) / 2;

        if ( rslt > div )
            rslt = div;
        else
            return ( uint16_t ) rslt;
    }
}


sqrt_new — sqrt_fpu ( 2).



sqrt_new:



  • ;
  • — sqrt_fpu;
  • ;


sqrt_new:



  • .


sqrt_new ( 2):



  • ;
  • .


2: sqtr_new (!)



(!) — 10e5+ 8 .



sqrt_new :



  • , , ( );
  • , -, ;
  • .


2. sqrt_evn ( 5).



sqrt_evn , , [ 0… 0xFFFFFFFF ].



sqrt_evn 2- 5- , sqrt_new ~40%.



[ 1… 10 000 000 ] sqtr_evn 2-3 .



sqrt_evn — 3.

3: sqtr_evn



, sqrt_evn — 5.

5: sqrt_evn



uint16_t sqrt_evn ( uint32_t L )
{
    if ( L < 2 )
        return ( uint16_t ) L;

    uint32_t div;
    uint32_t rslt;
    uint32_t temp;

    if ( L & 0xFFFF0000L )
        if ( L & 0xFF000000L )
            if ( L & 0xF0000000L )
                if ( L & 0xE0000000L )
                    div = 43771;
                else
                    div = 22250;
            else
                if ( L & 0x0C000000L )
                    div = 11310;
                else
                    div = 5749;
        else
            if ( L & 0x00F00000L )
                if ( L & 0x00C00000L )
                    div = 2923;
                else
                    div = 1486;
            else
                if ( L & 0x000C0000L )
                    div = 755;
                else
                    div = 384;
    else
        if ( L & 0xFF00L )
            if ( L & 0xF000L )
                if ( L & 0xC000L )
                    div = 195;
                else
                    div = 99;
            else
                if ( L & 0x0C00L )
                    div = 50;
                else
                    div = 25;
        else
            if ( L & 0xF0L )
                if ( L & 0x80L )
                    div = 13;
                else
                    div = 7;
            else
                div = 3;

    rslt = L;

    while ( 1 )
    {
        temp = L / div;
        temp += div;

        div = temp >> 1;
        div += temp & 1;

        if ( rslt > div )
            rslt = div;
        else
        {
            if ( L / rslt == rslt - 1 && L % rslt == 0 )
                rslt--;

            return ( uint16_t ) rslt;
        }
    }
}


«» — . 1 .



sqrt_evn , .

( 2).



— .



.

[ 3, 7, 13, 25 ] « ». (). .



— .





:



  • : STM32F0308-DISCO, MCU STM32F030R8T6
  • : STM32CubeIDE
  • : USB-UART PL2303HX


:



  • :
  • : CPU — 48 MHz, UART (RS485) — 9600 bit/s
  • : , Release
  • : MCU GCC Linker: Miscellaneous: -u _printf_float


sqrt_fpu, sqrt_new sqrt_evn.



100 000 3- — 4.

4:



.



— sqrt_fpu, . — .



, ( 4), .



( 5) .



5:



( 6) , 1 .

sqrt_fpu 19 531, sqrt_evn 147 059 ; sqrt_evn ~7,5 , sqrt_fpu.



6:





, , , .



Al mismo tiempo, la optimización algorítmica manual del código puede ser efectiva en la producción en masa de pequeñas IoT, debido al uso de modelos de microcontroladores de bajo costo, liberando espacio de tareas complejas para modelos más antiguos.




All Articles