stm32. Wir schauen auf die Wurzel

Anstatt vorzustellen



Der Artikel enthält ein Beispiel für die manuelle Optimierung eines kritischen Abschnitts eines Anwendungsprogramms für Budget-Stm32-Mikrocontroller, wodurch die Leistung im Vergleich zu einer Bibliotheksfunktion um das Fünffache oder mehr gesteigert wird.



Die Quadratwurzelextraktion wird häufig in Anwendungen verwendet. Die sqrt-Funktion ist in der Standard-C-Bibliothek enthalten und arbeitet mit reellen Zahlen:



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


Mikrocontroller arbeiten hauptsächlich mit ganzen Zahlen. Sie haben normalerweise keine Register für reelle Zahlen.



In der Praxis geht zusätzlich zum Verlust der Rechengeschwindigkeit bei mehreren Transformationen "Ganzzahl <=> real" zusätzlich die Genauigkeit verloren - Beispiel 1.



Beispiel 1: Präzisionsverlust bei Vorwärts- und Rückwärtskonvertierungen



//  
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%


Formulierung des Problems



Erhöhen Sie die Genauigkeit von sqrt-Berechnungen, indem Sie auf die nächste Ganzzahl runden.

Wenn möglich, steigern Sie die Produktivität.



Die Lösung des Problems



Erstellen Sie eine benutzerdefinierte Funktion, z. B. sqrt_fpu, basierend auf der Standardfunktion - Beispiel 2.



Beispiel 2: Berechnen einer Ganzzahlwurzel mit dem Algorithmus 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;
} 


Sqrt_fpu Vorteile:



  • kompakter Code;
  • Die erforderliche Genauigkeit wird erreicht.


Nachteile von sqrt_fpu:



  • Leistungsverlust aufgrund eines zusätzlichen Anrufs und zusätzlicher Gleitkommaoperationen;
  • Mangel an offensichtlichem Potenzial zur Optimierung der Rechengeschwindigkeit auf Benutzerebene.


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:





, , , .



Gleichzeitig kann die manuelle algorithmische Codeoptimierung aufgrund der Verwendung kostengünstiger Mikrocontrollermodelle bei der Massenproduktion kleiner IoT effektiv sein, wodurch der Raum für komplexe Aufgaben für ältere Modelle frei wird.




All Articles