Ottimizzazione delle operazioni di divisione |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dopo aver già sviluppato è collaudato da molto tempo le routine relative al movimento, mi è capitato di leggere gli articoli di Antonio Di Stefano sulla rivista "Fare Elettronica" di maggio e giugno 2005, riguardanti l'ottimzzazione del codice mediante l'uso dell'aritmetica "fixed point base two" cioè con virgola fissa in potenze di 2.
Nelle routine di movimento ci sono diversi calcoli. Per evitare l'uso dell'aritmetica floating point, troppo pesante per un microcontrollore non provvisto di FPU, usavo l'aritmetica fixed point in base 10: per avere la precisione di un decimale, moltiplicavo le costanti per 10 e alla fine dei calcoli dividevo il risultato per 10, per due decimali moltiplicavo e dividevo per 100 e così via, stando sempre attenti agli overflow. Dal momento che le costanti sono calcolate una volte per tutte in fase di definizione, nulla mi vieta di moltiplicarle, ad esempio, per 128 invece che per 100 e poi dividere per 128 il risultato finale. Ho ricontrollato tutto il codice e sono riuscito a togliere tutte le divisioni sostituendole con gli shift. Il tempo risparmiato è stato di diverse centinaia di microsecondi, la cosa mi fa molto comodo dal momento che la MCU è abbastanza stressata da diverse periferiche e soprattutto dagli impulsi continui dei due encoder in quadratura. Ma... c'è purtroppo un ma, quando ho provato ad accendere il robottino non funzionava più niente. Con l'aiuto proprio del Di Stefano ho provato alcune soluzioni diverse per ripristinare i bit di segno non propagati. short int i; // variabile a 16 bit La prima parte applica una maschera cancellando tutti i bit tranne il MSB, il numero così ottenuto viene messo in OR (cioè "sovrapposto") al numero shiftato, che avrà 0 come MSB, in questo modo si ottiene l'effetto voluto. Può essere comodo definire questa riga come macro. Ovviamente il numero 0x8000 è stato scelto perchè la variabile è a 16 bit (se fosse stato un char sarebbe stata 0x80, e così via...). (Grazie Antonio). Per ottimizzare invece i tempi di esecuzione, si può effettuare un unico shift delle posizioni volute e poi, nel caso di numero negativo, rimettere ad 1 con un OR i bit che sono diventati zero a causa dello shift stesso. ovviamente dividendo per 32 la maschera sarebbe 0xF800, dividendo per 64 0xFC00 e così via. Volendo essere molto precisi, il risultato della divisione normale rispetto allo shift non è lo stesso per i numeri negativi dispari, l'approssimazione in questo caso è all'intero inferiore invece che superiore: Bisogna anche stare attenti al cast automatico delle variabili: Nella modalità seguente invece il cast deve essere esplicitamente dichiarato prima di effettuare lo shift Insomma, l'ottimizzazione deve essere eseguita con attenzione sapendo bene cosa si va a toccare. Se il tempo di esecuzione non è un parametro critico nel nostro SW, conviene continuare ad usare le librerie matematiche standard del compilatore. Mentre facevo tutte queste prove mi è risultato abbastanza facile, ed è stato molto interessante, misurare con il cronometro del debugger dell'IDE MPLAB, i tempi di esecuzione nelle varie modalità. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
In questa pagina sono riportate le porzioni di codice coinvolte, prima e dopo l'ottimizzazione che ha portato al risparmio di alcune centinaia di microsecondi ad ogni ciclo.
Come si può vedere nei codici a confronto, durante la "ripulitura" ho migliorato i tempi anche su altri piccoli ma significativi particolari. #define kp 40 // costante errore proporzionale (fattore P) moltiplicato 16 che ovviamente non impiega alcun ciclo macchina, e: int const kp = 25; // costante errore proporzionale (fattore P) moltiplicato 10 che richiede parecchi microsecondi ogni volta che si entra nella procedura. Questa non me l'aspettavo! |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
aggiornato il 24 - 08 - 2005
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||