Michael Novák, UREL, FEEC, VUT Brno
xnovak58stud.feec.vutbr.cz
Abychom dosáhli úspěšného a správného výsledku měření, je potřeba vypnou veškerou optimalizaci kompilátoru zdrojového kódu. Tato optimalizace mění strukturu a pořadí samotného kódu, a dobu zpracování by tak nebylo možné v simulátoru změřit.
Veškeré naměřené hodnoty jsou tedy platné pro neoptimalizovaný kód, kde vstupy i výstupy matematických funkcí i základních matematických operací jsou paměťové proměnné. Měření bylo provedeno několikrát v jednom cyklu, vždy se stejným výsledkem.
Základní matematické operace představují jednoduché sčítání, odčítání, násobení, dělení a dělení modulo. Tyto operace lze provádět s jakýmkoliv číselným typem, a to se znaménkem i bez znaménka. V následující tabulce je vidět, že práce s celočíselnými typy je ve srovnání s typem s plovoucí desetinnou čárkou mnohem rychlejší. Mikrokontroléry Atmel AVR nejsou na výpočty s plovoucí desetinnu čárkou přizpůsobené.
Tab. 1: Doba zpracování jednoduchých matematických operací ve strojových cyklech
Operace | char | int | long | double | |||
unsigned | signed | unsigned | signed | unsigned | signed | ||
A++ | 5 | 5 | 10 | 10 | 20 | 20 | 983 |
B-- | 5 | 5 | 10 | 10 | 20 | 20 | 961 |
C = A + B | 7 | 8 | 14 | 14 | 28 | 28 | 890 |
C = A - B | 9 | 10 | 16 | 16 | 32 | 32 | 859 |
C = A * B | 10 | 10 | 23 | 23 | 76 | 76 | 2047 |
C = A / B | 87 | 258 | 212 | 256 | 601 | 656 | 1395 |
C = A mod B | 88 | 257 | 211 | 255 | 601 | 656 | - |
Mezi pokročilé matematické funkce patří např. goniometrické funkce a statistické funkce. Většina těchto funkcí pracuje s parametry s plovoucí desetinnou čárkou (typ double), a proto jsou výpočetně náročné. Ke srovnání výkonu jsou zde uvedeny hodnoty testu z manuálu avr-libc [1] pro CPU jádra AVR2 a AVR4.
Tab. 2: Doba zpracování pokročilých matematických funkcí ve strojových cyklech
Funkce | Definice | Strojových cyklů | Poznámka | |||
Aktuální | AVR2 | AVR4 | ||||
O = cos (M) | double | cos (double __x) | 1742 | 3387 | 1671 | |
O = abs (M) | double | fabs (double __x) | 17 | - | - | |
O = M mod N | double | fmod (double __x, double __y) | 244 | 131 | 131 | |
O = M mod 1, N = M div 1 = M - O |
double | modf (double __x, double *__iptr) | 1553 | 433 | 429 | Rozdělí číslo na celou a desetinnou část |
O = sin (M) | double | sin (double __x) | 1819 | 3353 | 1653 | |
O = sqrt (M) | double | sqrt (double __x) | 496 | 494 | 492 | |
O = tan (M) | double | tan (double __x) | 2430 | 4381 | 2426 | |
O = floor (M) | double | floor (double __x) | 119 | 180 | 180 | |
O = ceil (M) | double | ceil (double __x) | 122 | 177 | 177 | |
O * 2^N = M | double | frexp (double __x, int *__pexp) | 65 | 42 | 41 | |
O = M * 2^N | double | ldexp (double __x, int __exp) | 68 | 42 | 42 | |
O = exp (M) | double | exp (double __x) | 54 | 4708 | 2765 | |
O = cosh (M) | double | cosh (double __x) | 647 | 4922 | 2979 | |
O = sinh (M) | double | sinh (double __x) | 670 | 4946 | 3003 | |
O = tanh (M) | double | tanh (double __x) | 1751 | 5126 | 3173 | |
O = acos (M) | double | acos (double __x) | 2691 | 4411 | 2455 | |
O = asin (M) | double | asin (double __x) | 2761 | 4517 | 2556 | |
O = atan (M) | double | atan (double __x) | 3233 | 4710 | 2271 | |
O = atan (M / N) | double | atan2 (double __y, double __x) | 3729 | 5270 | 2857 | |
O = log (M) | double | log (double __x) | 3039 | 4142 | 2134 | přirozený logaritmus |
O = log10 (M) | double | log10 (double __x) | 3165 | 4498 | 2260 | dekadický logaritmus |
O = power (M, N) = M ^ N | double | pow (double __x, double __y) | 6825 | 9293 | 5047 | |
O = isNaN (M) | int | isnan (double __x) | 355 | - | - | vrací nenulovou hodnotu pokud parametr M není číslo |
O = isInf (M) | int | isinf (double __x) | 826 | - | - | vrací nenulovou hodnotu pokud parametr je nekonečno |
O = sqr (M) | double | square (double __x) | 151 | - | - | square root |
O = (abs (M) * sign (N)) | static double | copysign (double __x, double __y) | 112 | - | - | vrací M se znaménkem N |
O = max (M - N, 0) | double | fdim (double __x, double __y) | 851 | 111 | 111 | |
O = (M * N) + P | double | fma (double __x, double __y, double __z) | 270 | - | - | |
O = max (M, N) | double | fmax (double __x, double __y) | 58 | 39 | 37 | |
O = min (M, N) | double | fmin (double __x, double __y) | 61 | 35 | 35 | |
O = sign (M) | int | signbit (double __x) | 17 | - | - | |
O = trunc (M) | double | trunc (double __x) | 117 | 178 | 178 | |
O = isFinite (M) | static int | isfinite (double __x) | 86 | - | - | |
O = sqrt (M^2 + N^2) | double | hypot (double __x, double __y) | 852 | 1341 | 866 | |
O = round (M) | double | round (double __x) | 96 | 150 | 150 | |
O = round (M) | long | lround (double __x) | 76 | - | - | |
O = round (M) | long | lrint (double __x) | 89 | - | - |
Na první pohled je patrné, že srovnání mého měření a měření z oficiálních stránek [1] jsou velice odlišné. Ve skutečnosti to ale závisí na typu použitého kompilátoru, metodě použité optimalizace kódu. Průběh výpočtu některých funkcí je také závislý na hodnotě parametru, která může ovlivnit dobu jeho zpracování.