Proveďte srovnání časové a paměťové náročnosti matematických výpočtů goniometrických funkcí, vektorového násobení a konvoluce na jádru Cortex-M4. Využijte funkce dostupné v <arm_math.h>, použijte fixed-point a float aritmetiky, vyzkoušejte soft a hard FPU koprocesor.
V tomto projektu byla použita vývojová deska STM32F4 DISCOVERY, která je osazena mikrokontrolérem STM32F407VGT6. Zmíněný mikrokontrolér obsahuje jádro ARM Cortex-M4 včetně FPU koprocesoru. Kromě vývojové desky není pro tento projekt potřeba žádný další hardware.
Vytvořená funkce pro testování doby výpočtu se nachází v knihovně <benchmark.h>
. Čas je měřen pomocí 32-bitového časovače TIM 2. Přetečení časovače není ošetřeno, protože všechny výpočty proběhnou o dost dříve, než přeteče samotný časovač. Časovač přeteče přibližně každých 50 sekund. Výsledky program zobrazí na standartní výstup. V tomto projektu bylo použito přesměrování standartního výstupu do debuggeru pomocí semihostingu.
Pro funkci knihovny <arm_math.h>
je potřeba přidat do projektu soubor knihovny pro příslušný procesor. V našem případě je to soubor libarm_cortexM4lf_math.a pro výpočty s pomocí FPU koprocesoru. Soubor libarm_cortexM4l_math.a slouží k výpočtům bez využití FPU koprocesoru. Program se však pro výpočty bez využití FPU koprocesoru nepodařilo zprovoznit.
Níže je zobrazen soubor benchmark.c vytvořené knihovny.
/* Knihovna <benchmark.h> slouzi k otestovani doby vypoctu nekterych funkci z knihovny <arm_math.h>. * * Funkce PrintTime zobrazi na standartnim vystupu cas v us vypocitany z rozdilu t1 a t2. * TrigonometricFunctionsTest provede mereni casu vypoctu goniometrickych funkci. * VectorMultiplicationTest provede mereni casu vypoctu funkci na vektorove nasobeni. * ConvolutionTest provede mereni casu vypoctu funkci konvoluce. */ #include "stm32f4xx_hal.h" #include <math.h> #include <stdio.h> #include <stdlib.h> #include "arm_math.h" #define MEAS_ERR 0.096 //chyba vznikla pri mereni casu casovacem v us TIM_HandleTypeDef htim2; void PrintTime(uint32_t t1, uint32_t t2) { float32_t time_us = (t2 - t1) * 1000000.0 / (SystemCoreClock / 2) - MEAS_ERR; printf("doba = %.2f us\n", time_us); printf(" \n"); } void TrigonometricFunctionsTest(void) { //promenne k mereni casu uint32_t t1; uint32_t t2; //promenne k vypoctum float32_t fx = 0.222; float32_t fy; q31_t qqx = 536870911; q31_t qqy; q15_t qx = 8191; q15_t qy; //cos float t1 = __HAL_TIM_GetCounter(&htim2); fy = arm_cos_f32(fx); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_cos_f32\n"); PrintTime(t1, t2); //sin float t1 = __HAL_TIM_GetCounter(&htim2); fy = arm_sin_f32(fx); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_sin_f32\n"); PrintTime(t1, t2); //cos q31 t1 = __HAL_TIM_GetCounter(&htim2); qqy = arm_cos_q31(qqx); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_cos_q31\n"); PrintTime(t1, t2); //sin q31 t1 = __HAL_TIM_GetCounter(&htim2); qqy = arm_sin_q31(qqx); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_sin_q31\n"); PrintTime(t1, t2); //cos q15 t1 = __HAL_TIM_GetCounter(&htim2); qy = arm_cos_q15(qx); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_cos_q15\n"); PrintTime(t1, t2); //sin q15 t1 = __HAL_TIM_GetCounter(&htim2); qy = arm_sin_q15(qx); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_sin_q15\n"); PrintTime(t1, t2); //cos float math.h t1 = __HAL_TIM_GetCounter(&htim2); fy = cos(fx); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: cos\n"); PrintTime(t1, t2); //sin float math.h t1 = __HAL_TIM_GetCounter(&htim2); fy = sin(fx); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: sin\n"); PrintTime(t1, t2); //pouziti nepouzitych promennych qy++; qqy++; fy++; } void VectorMultiplicationTest(void) { //promenne k mereni casu uint32_t t1; uint32_t t2; //promenne k vypoctum float32_t fvx[1024]; float32_t fvy[1024]; float32_t fvz[1024]; q31_t qqqvx[1024]; q31_t qqqvy[1024]; q31_t qqqvz[1024]; q15_t qqvx[1024]; q15_t qqvy[1024]; q15_t qqvz[1024]; q7_t qvx[1024]; q7_t qvy[1024]; q7_t qvz[1024]; //naplneni nejakymi daty k vypoctu for(uint16_t i = 0; i < 1024; i++) { fvx[i] = i; fvy[i] = PI; qqqvx[i] = i; qqqvy[i] = 123456789; qqvx[i] = i; qqvy[i] = 1234; qvx[i] = i % 50; qvy[i] = 12; } //vektorove nasobeni float 1024, 256, 64, 8 prvku uint32_t blockSize[] = {1024, 256, 64, 8}; for(uint16_t i = 0; i < 4; i++) { t1 = __HAL_TIM_GetCounter(&htim2); arm_mult_f32(fvx, fvy, fvz, blockSize[i]); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_mult_f32, delka vektoru = %lu\n", (unsigned long)blockSize[i]); PrintTime(t1, t2); } //vektorove nasobeni q31 1024, 256, 64, 8 prvku for(uint16_t i = 0; i < 4; i++) { t1 = __HAL_TIM_GetCounter(&htim2); arm_mult_q31(qqqvx, qqqvy, qqqvz, blockSize[i]); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_mult_q31, delka vektoru = %lu\n", (unsigned long)blockSize[i]); PrintTime(t1, t2); } //vektorove nasobeni q15 1024, 256, 64, 8 prvku for(uint16_t i = 0; i < 4; i++) { t1 = __HAL_TIM_GetCounter(&htim2); arm_mult_q15(qqvx, qqvy, qqvz, blockSize[i]); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_mult_q15, delka vektoru = %lu\n", (unsigned long)blockSize[i]); PrintTime(t1, t2); } //vektorove nasobeni q7 1024, 256, 64, 8 prvku for(uint16_t i = 0; i < 4; i++) { t1 = __HAL_TIM_GetCounter(&htim2); arm_mult_q7(qvx, qvy, qvz, blockSize[i]); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_mult_q7, delka vektoru = %lu\n", (unsigned long)blockSize[i]); PrintTime(t1, t2); } } void ConvolutionTest(void) { //promenne k mereni casu uint32_t t1; uint32_t t2; //promenne k vypoctum float32_t *fvx; float32_t *fvy; float32_t *fvz; q31_t *qqqvx; q31_t *qqqvy; q31_t *qqqvz; q15_t *qqvx; q15_t *qqvy; q15_t *qqvz; q7_t *qvx; q7_t *qvy; q7_t *qvz; //konvoluce float 1024, 256, 64, 8 prvku fvx = (float32_t*) malloc(1024 * sizeof(float32_t)); fvy = (float32_t*) malloc(1024 * sizeof(float32_t)); fvz = (float32_t*) malloc(2047 * sizeof(float32_t)); srand(22); //naplneni nahodnymi daty k vypoctu for(uint16_t i = 0; i < 1024; i++) { fvx[i] = rand() - 100.1234; fvy[i] = rand() - 200.5678; } uint32_t blockSize[] = {1024, 256, 64, 8}; for(uint16_t i = 0; i < 4; i++) { t1 = __HAL_TIM_GetCounter(&htim2); arm_conv_f32(fvx, blockSize[i], fvy, blockSize[i], fvz); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_conv_f32, delka vektoru = %lu\n", (unsigned long)blockSize[i]); PrintTime(t1, t2); } free(fvx); free(fvy); free(fvz); //konvoluce q31 1024, 256, 64, 8 prvku qqqvx = (q31_t*) malloc(1024 * sizeof(q31_t)); qqqvy = (q31_t*) malloc(1024 * sizeof(q31_t)); qqqvz = (q31_t*) malloc(2047 * sizeof(q31_t)); //naplneni nahodnymi daty k vypoctu for(uint16_t i = 0; i < 1024; i++) { qqqvx[i] = rand(); qqqvy[i] = rand(); } for(uint16_t i = 0; i < 4; i++) { t1 = __HAL_TIM_GetCounter(&htim2); arm_conv_q31(qqqvx, blockSize[i], qqqvy, blockSize[i], qqqvz); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_conv_q31, delka vektoru = %lu\n", (unsigned long)blockSize[i]); PrintTime(t1, t2); } free(qqqvx); free(qqqvy); free(qqqvz); //konvoluce q15 1024, 256, 64, 8 prvku qqvx = (q15_t*) malloc(1024 * sizeof(q15_t)); qqvy = (q15_t*) malloc(1024 * sizeof(q15_t)); qqvz = (q15_t*) malloc(2047 * sizeof(q15_t)); //naplneni nahodnymi daty k vypoctu for(uint16_t i = 0; i < 1024; i++) { qqvx[i] = rand() % 500; qqvy[i] = rand() % 500; } for(uint16_t i = 0; i < 4; i++) { t1 = __HAL_TIM_GetCounter(&htim2); arm_conv_q15(qqvx, blockSize[i], qqvy, blockSize[i], qqvz); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_conv_q15, delka vektoru = %lu\n", (unsigned long)blockSize[i]); PrintTime(t1, t2); } free(qqvx); free(qqvy); free(qqvz); //konvoluce q7 1024, 256, 64, 8 prvku qvx = (q7_t*) malloc(1024 * sizeof(q7_t)); qvy = (q7_t*) malloc(1024 * sizeof(q7_t)); qvz = (q7_t*) malloc(2047 * sizeof(q7_t)); //naplneni nahodnymi daty k vypoctu for(uint16_t i = 0; i < 1024; i++) { qvx[i] = rand() % 20; qvy[i] = rand() % 20; } for(uint16_t i = 0; i < 4; i++) { t1 = __HAL_TIM_GetCounter(&htim2); arm_conv_q7(qvx, blockSize[i], qvy, blockSize[i], qvz); t2 = __HAL_TIM_GetCounter(&htim2); printf("test: arm_conv_q7, delka vektoru = %lu\n", (unsigned long)blockSize[i]); PrintTime(t1, t2); } free(qvx); free(qvy); free(qvz); }
V této části se nachází výsledky měření doby výpočtu požadovaných funkcí z knihovny <arm_math.h>
. Měření bylo provedeno pouze s hard FPU koprocesorem. Pro soft FPU koprocesor se nepodařilo program rozběhnout. Pro porovnání byly změřeny i funkce cos
a sin
z knihovny <math.h>
. Frekvence procesoru byla nastavena na 168 MHz. Optimalizace kompilátoru nebyly použité.
funkce | doba výpočtu |
---|---|
arm_cos_f32 | 0.45 us |
arm_sin_f32 | 0.45 us |
arm_cos_q31 | 0.33 us |
arm_sin_q31 | 0.33 us |
arm_cos_q15 | 0.38 us |
arm_sin_q15 | 0.33 us |
cos | 10.21 us |
sin | 8.59 us |
počet prvků vstupních vektorů | arm_mult_f32 | arm_mult_q31 | arm_mult_q15 | arm_mult_q7 |
---|---|---|---|---|
1024 | 44.64 us | 85.90 us | 50.90 us | 63.02 us |
256 | 11.50 us | 21.90 us | 13.19 us | 16.17 us |
64 | 3.21 us | 5.90 us | 3.76 us | 4.45 us |
8 | 0.81 us | 1.24 us | 1.00 us | 1.05 us |
počet prvků vstupních vektorů | arm_conv_f32 | arm_conv_q31 | arm_conv_q15 | arm_conv_q7 |
---|---|---|---|---|
1024 | 39310.40 us | 33176.83 us | 159415.36 us | 43162.62 us |
256 | 2510.31 us | 2148.12 us | 9997.67 us | 2742.38 us |
64 | 170.95 us | 153.62 us | 633.33 us | 183.38 us |
8 | 5.62 us | 6.19 us | 11.50 us | 5.78 us |
Zadání bylo splněno pouze z části, protože se mi nepodařilo vytvořit program nevyužívající FPU koprocesor a také není zjištěna paměťová náročnost funkcí. Zkoušel jsem přidat do projektu soubor libarm_cortexM4l_math.a, vypnout v nastavení kompilátoru FPU a odstranit z #defines __FPU_USED. Program však nešel zkompilovat. Pro zjištění paměťové náročnosti jsem zkoušel alokovat pole o maximální velikosti co šlo (musel jsem postupně v cyklu zkoušet postupně alokovat více a více místa), poté bylo pole naplněno stejnými konstantami, následně byla paměť uvolněna. Po provedení výpočtů bylo pole opět alokováno a zjištěno, kolik se v poli nachází původních konstant. Toto řešení mi ovšem ukazovalo využitou paměť chybně.
Zjištěné doby výpočtů u funkce arm_conv_q15
jsou oproti ostatním časům podezřele velké.
Výsledky podobného měření doby výpočtu funkcí z knihovny <arm_math.h>
na jiném procesoru lze vidět například zde: K70 DSP benchmark test.