Documente Academic
Documente Profesional
Documente Cultură
Cuprins:
Tehnici de programare robusta
Optimizarea programelor C
Bibliografie
Joseph Lemieux, Programming in the OSEK/VDX Environment, Elsevier, 2001
Richard Zurawski, Embedded Systems Handbook, CRC Press, 2006
V. REALIZAREA APLICAŢIILOR SOFT
PENTRU SISTEME INCORPORATE
V. 1. Implementarea unor programe C robuste
Definitii
Eficienta: o masura a gradului in care resursele de calcul sunt utilizate: timp executie,
consum de memorie – pentru cod si date (ROM, FLASH/ RAM, EEPROM).
Motivatia pentru o programare robusta:
- costuri!!!!
- siguranta!!
Observatii:
- multe anomalii pot fi evitate aplicand tehnici de programare deja stiute
- foloseste ANSI C pentru portabilitate (extensiile de limbaj conduc de regula la cod
neportabil)
Standard Motor Industry Reliability Association (MISRA)
– aprilie 1998
www.misra.org.uk
!!! de preferat ca aceste reguli sa fie respectate pe masura ce codul este scris (nu
doar sa fie refacut codul la sfarsit in baza MIRSA)
- este preferabil sa aplici aceste reguli in timp ce scrii codul (ar putea aparea erori la
modificari viitoare)
- verifica specificatiile (50% din erori sunt generate de folosirea gresita a specificatiilor –
presupuneri false, etc )
- testeaza codul des, cam la fiecare 15 linii de cod scrise (in medie va aparea o eroare)
- foloseste un stil “defensiv”: verifica stările de eroare returnate de funcţii, verifica faptul ca ai
inclus default la switch, nu fa presupuneri nefondate (stari implicite, valori biti, intarzieri,
etc)
- evita codul eliptic, greu de inteles >> cod cat mai simplu de inteles
>> sugestii
functii: predicat+subiect
ex: GetID(…), CalculateResponse(…), TreatInputRequests(…)
nume usor de inteles, reguli consistente
- declara variabilele pentru cel mai redus domeniu de valabilitate necesar (functie, fisier)
MISRA Rule 22 (recomandare) >> lizibilitate && reduce acces nepermis/conflict
>> exceptie: 0
>> asigura excludere mutuala pentru fiecare (atentie, intreruperile asincrone pot aparea
oricand)
>> tehnici:
MISRA Rule 71
- actualizeaza starea/iesirea porturilor (nu presupune ca odata setata, starea este stabila)
V.2.3 Optimizare cod C unele reguli au fost rezolvate implicit in unele compilatoare/PC
a) Declaraţii, tipuri de variabile
- Aritmetica pe întregi este mai rapidă decât aritmetica în virgulă mobilă. Aritmetica pe float
este mai rapidă ca pe double
o nu folosi float dacă o variabilă ia doar valori intregi
o poti folosi rotunjiri
Ex: dacă doreşti 3 zecimale exacte, înmulţeşte toate valorile numerice cu 1000,
foloseşte calcul pe întregi şi la final rescalează prin împărţirea la 1000
!!! In prezenta FPU, calculele in float sunt rapid executate si poti folosi FLOAT!!!
- Dacă o variabila este număr natural foloseste unsigned int (astfel unele procesoare
lucrează mai rapid) şi chiar register unsigned int pentru variabilele des folosite
(acces mai rapid – dacă există registre libere)
- Foloseşte variabile cu lungime 2, 4 octeti (multiplu de cuvinte): int, float
Nu folosi des char, short, double, câmpuri de biţi etc. mai ales la variabilele
locale. La char compilatorul trebuie să reducă dimensiunea rezultatelor de tip int
switch(c){ switch(c){
case 'A':{do something; case 0:{do something;
break;} break;}
case 'H':{do something; case 1:{do something;
break;} break;}
case 'Z'{do something; case 2:{do something;
break;} break;}
} }
- Accesul la tablourile unidimensionale este mai rapid ca accesul la tablouri
multidimensionale
o Dacă o functie ce accesează mult memoria foloseste două tablouri diferite – acestea
pot fi concatenate într-un singur tablou mai mare
o Dacă dintr-un tabel numai o parte este frecvent folosită, aceasta poate fi separată
într-un tablou distinct (pentru a aduce tot tabloul util in memoria cache)
- Accesul la o structura de tablouri este mai rapid ca accesul la un tablou de structuri (utilizare
mai buna a memoriei cache de către compilator)
- In tablourile de structuri este bine ca o structură să aibă lungimea egală cu o putere a lui 2
(în octeţi), pentru a asigura o indexare optimă în tablou
- Nu folosi variabile globale, dacă nu e necesar (variabilele globale nu sunt alocate unor
regiştri – cel mai rapid acces nu poate fi asigurat);
- O variabilă locală să aibă cel mai mic domeniu de valabilitate necesar (fără declaraţii în
exteriorul funcţiilor care o folosesc)
b) Instrucţiuni de test
if (x != 0) x = 0; x = 0;
- Poţi reduce numărul de reluari ale unei instrucţiuni repetitive la care numărul mare de reluări
nu este cunoscut a priori
#include<stdio.h>
#define BLOCKSIZE 8
void main(void){
int i = 0;
int limit = 33;
int blocklimit;
blocklimit = (limit/BLOCKSIZE)*BLOCKSIZE;
while( i < blocklimit ){
printf("process(%d)\n", i); printf("process(%d)\n", i+1);
printf("process(%d)\n", i+2); printf("process(%d)\n", i+3);
printf("process(%d)\n", i+4); printf("process(%d)\n", i+5);
printf("process(%d)\n", i+6); printf("process(%d)\n", i+7); i += 8;}
if( i < limit ){ switch( limit - i ){
case 7 : printf("process(%d)\n", i); i++;
case 6 : printf("process(%d)\n", i); i++;
case 5 : printf("process(%d)\n", i); i++;
case 4 : printf("process(%d)\n", i); i++;
case 3 : printf("process(%d)\n", i); i++;
case 2 : printf("process(%d)\n", i); i++;
case 1 : printf("process(%d)\n", i);}}}
- Foloseşte instrucţiuni nerepetitive echivalente, dacă este posibil
- Evită folosirea ++ sau – - la condiţiile de test (ex: while(n- - ) {}) – sunt greu de
optimizat
- Foloseşte break sau alte variante pentru a ieşi din bucle dacă anumite condiţii sunt
îndeplinite (dar atentie, programele structurate pot fi in general mai rapide)
found = FALSE;
found = FALSE; for(i=0; i<10000; i++)
for(i=0;i<10000;i++) if( list[i] == -99 ){
if( list[i] == -99 ) found = TRUE;
found = TRUE;} break; }
if( found ) if( found )
printf("am gasit -99\n"); printf("am gasit -99\n");
//-99 gasit pentru i= 23
c) Funcţii
Argumentele funcţiilor
- Foloseşte maxim k argumente, k = nr reg generali (pasare parametri folosind registrii - mai
rapid)
o Grupează parametrii într-o structura + pasează pointerul la structură.
o Evită funcţiile cu număr variabil de parametri (pasarea parametrilor se face numai
prin stivă)
o Foloseşte puţine argumente long, double
- Nu pasa parametri intrare de tip pointeri pentru functii care nu altereaza continutul datelor
(compilatorul poate face anumite optimizări, daca nu este fortat ca la fiecare apel sa
recitească data respectiva din memorie)
void func1( int *data ) void func1( int *data )
{ {
int i; int i;
for(i=0; i<10; i++){ int localdata;
somefunc2( *data, i);} localdata = *data;
} for(i=0; i<10; i++){
somefunc2( localdata, i);}
//somefucn2 nu altereaza }
//info pointata de data
- Pasează structurile NUMAI prin referinţe/pointeri. Pasarea structurilor prin valoare
presupune copierea acestora în stivă (structurile pot fi de mari dimensiuni!!!!!!).
o Dacă funcţia nu schimbă conţinutul structurii, argumentul este bine să fie de tip
pointer la o structura constantă (astfel, compilatorul va proteja conţinutul structurii
de modificări nedorite).
- Nu folosi excesiv de multe funcţii (deşi de obicei timpul necesar pentru a le preda controlul
<< timpul necesar pentru execuţia funcţiei)
- Cele mai des apelate funcţii să fie de tip frunză („leaf”) – să nu apeleze alte funcţii – pentru
acestea compilatorul poate face anumite optimizări
- Nu folosi în corpul unei funcţii multe variabile (permiţi astfel manipularea lor prin regiştri)
- Foloseşte şi macrouri #define….. (atenţie, astfel lungimea codului creşte şi poti reduce
viteza de execuţie prin depăşirea memoriei cash!!!)
- Declară functiile de tip static dacă e posibil (nu sunt folosite in alte fisiere - > module)
- Dacă este posibil, foloseste din biblioteci funcţiile care au timpi de execuţie mai mici:
o puts() este mai rapidă decât printf(), dar şi mai puţin flexibilă
o funcţiile care permit citirea binara, neformatată sunt mai rapide decât cele care
asigură citire formatată (de exemplu citire din fisiere)
- evită folosirea sqrt() în bucle (timp execuţie mare) (cu FPU e rapid)
- evită folosirea exp, sin, log - timp execuţie mare (implementate folosind
descompunerea în serii) - (cu FPU e rapid)
float a, b, c, d, f, g; float a, b, c, d, f, g;
a = b / c * d; a = b / c * d;
f = b * g / c; f = b / c * g;
- dacă anumite calcule sunt reluate de multe ori (exemplu sin0, cos35), este bine să efectuati
aceste calcule o singură dată, să treceţi rezultatele în nişte tabele şi să le folosiţi ulterior (ex
tabele cu valori ale funcţiilor trigonometrice, log, exp, factorial etc)
- foloseşte expresii simple şi scurte pentru a permite manipularea doar prin registri, fără
salvări temporare în memorie.
Sugestii. Recomandari de imbunatatire a cursului.